use proc_macro2::TokenStream; use quote::quote; use syn::{ parenthesized, parse::{Parse, ParseStream}, Token, }; use crate::diagnostic::DiagnosticVariant; #[derive(Debug)] pub struct Code(pub String); impl Parse for Code { fn parse(input: ParseStream) -> syn::Result { let ident = input.parse::()?; if ident == "code" { let la = input.lookahead1(); if la.peek(syn::token::Paren) { let content; parenthesized!(content in input); let la = content.lookahead1(); if la.peek(syn::LitStr) { let str = content.parse::()?; Ok(Code(str.value())) } else { let path = content.parse::()?; Ok(Code( path.segments .iter() .map(|s| s.ident.to_string()) .collect::>() .join("::"), )) } } else { input.parse::()?; Ok(Code(input.parse::()?.value())) } } else { Err(syn::Error::new(ident.span(), "diagnostic code is required. Use #[diagnostic(code = ...)] or #[diagnostic(code(...))] to define one.")) } } } impl Code { pub(crate) fn gen_enum(variants: &[DiagnosticVariant]) -> Option { let code_pairs = variants.iter().map( |DiagnosticVariant { ref ident, ref code, ref fields, .. }| { let code = &code.0; match fields { syn::Fields::Named(_) => { quote! { Self::#ident { .. } => std::boxed::Box::new(#code), } } syn::Fields::Unnamed(_) => { quote! { Self::#ident(..) => std::boxed::Box::new(#code), } } syn::Fields::Unit => quote! { Self::#ident => std::boxed::Box::new(#code), }, } }, ); Some(quote! { fn code<'a>(&'a self) -> std::boxed::Box { match self { #(#code_pairs)* } } }) } pub(crate) fn gen_struct(&self) -> Option { let code = &self.0; Some(quote! { fn code<'a>(&'a self) -> std::boxed::Box { std::boxed::Box::new(#code) } }) } }