1 use proc_macro2::{Span, TokenStream};
2 use quote::quote;
3 use syn::{
4     parenthesized,
5     parse::{Parse, ParseStream},
6     Token,
7 };
8 
9 use crate::{
10     diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
11     forward::WhichFn,
12     utils::gen_all_variants_with,
13 };
14 
15 pub struct Severity(pub syn::Ident);
16 
17 impl Parse for Severity {
parse(input: ParseStream) -> syn::Result<Self>18     fn parse(input: ParseStream) -> syn::Result<Self> {
19         let ident = input.parse::<syn::Ident>()?;
20         if ident == "severity" {
21             let la = input.lookahead1();
22             if la.peek(syn::token::Paren) {
23                 let content;
24                 parenthesized!(content in input);
25                 let la = content.lookahead1();
26                 if la.peek(syn::LitStr) {
27                     let str = content.parse::<syn::LitStr>()?;
28                     let sev = get_severity(&str.value(), str.span())?;
29                     Ok(Severity(syn::Ident::new(&sev, str.span())))
30                 } else {
31                     let ident = content.parse::<syn::Ident>()?;
32                     let sev = get_severity(&ident.to_string(), ident.span())?;
33                     Ok(Severity(syn::Ident::new(&sev, ident.span())))
34                 }
35             } else {
36                 input.parse::<Token![=]>()?;
37                 let str = input.parse::<syn::LitStr>()?;
38                 let sev = get_severity(&str.value(), str.span())?;
39                 Ok(Severity(syn::Ident::new(&sev, str.span())))
40             }
41         } else {
42             Err(syn::Error::new(
43                 ident.span(),
44                 "MIETTE BUG: not a severity option",
45             ))
46         }
47     }
48 }
49 
get_severity(input: &str, span: Span) -> syn::Result<String>50 fn get_severity(input: &str, span: Span) -> syn::Result<String> {
51     match input.to_lowercase().as_ref() {
52         "error" | "err" => Ok("Error".into()),
53         "warning" | "warn" => Ok("Warning".into()),
54         "advice" | "adv" | "info" => Ok("Advice".into()),
55         _ => Err(syn::Error::new(
56             span,
57             "Invalid severity level. Only Error, Warning, and Advice are supported.",
58         )),
59     }
60 }
61 
62 impl Severity {
gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream>63     pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
64         gen_all_variants_with(
65             variants,
66             WhichFn::Severity,
67             |ident, fields, DiagnosticConcreteArgs { severity, .. }| {
68                 let severity = &severity.as_ref()?.0;
69                 let fields = match fields {
70                     syn::Fields::Named(_) => quote! { { .. } },
71                     syn::Fields::Unnamed(_) => quote! { (..) },
72                     syn::Fields::Unit => quote! {},
73                 };
74                 Some(
75                     quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), },
76                 )
77             },
78         )
79     }
80 
gen_struct(&self) -> Option<TokenStream>81     pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
82         let sev = &self.0;
83         Some(quote! {
84             fn severity(&self) -> std::option::Option<miette::Severity> {
85                 Some(miette::Severity::#sev)
86             }
87         })
88     }
89 }
90