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