miette_derive/
code.rs

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}