1 use proc_macro2::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 #[derive(Debug)]
16 pub struct Code(pub String);
17 
18 impl Parse for Code {
parse(input: ParseStream) -> syn::Result<Self>19     fn parse(input: ParseStream) -> syn::Result<Self> {
20         let ident = input.parse::<syn::Ident>()?;
21         if ident == "code" {
22             let la = input.lookahead1();
23             if la.peek(syn::token::Paren) {
24                 let content;
25                 parenthesized!(content in input);
26                 let la = content.lookahead1();
27                 if la.peek(syn::LitStr) {
28                     let str = content.parse::<syn::LitStr>()?;
29                     Ok(Code(str.value()))
30                 } else {
31                     let path = content.parse::<syn::Path>()?;
32                     Ok(Code(
33                         path.segments
34                             .iter()
35                             .map(|s| s.ident.to_string())
36                             .collect::<Vec<_>>()
37                             .join("::"),
38                     ))
39                 }
40             } else {
41                 input.parse::<Token![=]>()?;
42                 Ok(Code(input.parse::<syn::LitStr>()?.value()))
43             }
44         } else {
45             Err(syn::Error::new(ident.span(), "diagnostic code is required. Use #[diagnostic(code = ...)] or #[diagnostic(code(...))] to define one."))
46         }
47     }
48 }
49 
50 impl Code {
gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream>51     pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
52         gen_all_variants_with(
53             variants,
54             WhichFn::Code,
55             |ident, fields, DiagnosticConcreteArgs { code, .. }| {
56                 let code = &code.as_ref()?.0;
57                 Some(match fields {
58                     syn::Fields::Named(_) => {
59                         quote! { Self::#ident { .. } => std::option::Option::Some(std::boxed::Box::new(#code)), }
60                     }
61                     syn::Fields::Unnamed(_) => {
62                         quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), }
63                     }
64                     syn::Fields::Unit => {
65                         quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), }
66                     }
67                 })
68             },
69         )
70     }
71 
gen_struct(&self) -> Option<TokenStream>72     pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
73         let code = &self.0;
74         Some(quote! {
75             fn code(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> {
76                 std::option::Option::Some(std::boxed::Box::new(#code))
77             }
78         })
79     }
80 }
81