1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{
4 parenthesized,
5 parse::{Parse, ParseStream},
6 Token,
7};
8
9use crate::{
10 diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
11 forward::WhichFn,
12 utils::gen_all_variants_with,
13};
14
15#[derive(Debug)]
16pub struct Code(pub String);
17
18impl Parse for Code {
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
50impl Code {
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
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}