use proc_macro2::TokenStream; use quote::quote; use syn::{ parenthesized, parse::{Parse, ParseStream}, Token, }; use crate::{ diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, forward::WhichFn, utils::gen_all_variants_with, }; #[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: &[DiagnosticDef]) -> Option { gen_all_variants_with( variants, WhichFn::Code, |ident, fields, DiagnosticConcreteArgs { code, .. }| { let code = &code.as_ref()?.0; Some(match fields { syn::Fields::Named(_) => { quote! { Self::#ident { .. } => std::option::Option::Some(std::boxed::Box::new(#code)), } } syn::Fields::Unnamed(_) => { quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(#code)), } } syn::Fields::Unit => { quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(#code)), } } }) }, ) } pub(crate) fn gen_struct(&self) -> Option { let code = &self.0; Some(quote! { fn code(&self) -> std::option::Option> { std::option::Option::Some(std::boxed::Box::new(#code)) } }) } }