1 use proc_macro2::TokenStream;
2 use quote::{format_ident, quote};
3 use syn::spanned::Spanned;
4 
5 use crate::{
6     diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
7     forward::WhichFn,
8     utils::{display_pat_members, gen_all_variants_with},
9 };
10 
11 pub struct SourceCode {
12     source_code: syn::Member,
13 }
14 
15 impl SourceCode {
from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>>16     pub fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
17         match fields {
18             syn::Fields::Named(named) => Self::from_fields_vec(named.named.iter().collect()),
19             syn::Fields::Unnamed(unnamed) => {
20                 Self::from_fields_vec(unnamed.unnamed.iter().collect())
21             }
22             syn::Fields::Unit => Ok(None),
23         }
24     }
25 
from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>>26     fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> {
27         for (i, field) in fields.iter().enumerate() {
28             for attr in &field.attrs {
29                 if attr.path().is_ident("source_code") {
30                     let source_code = if let Some(ident) = field.ident.clone() {
31                         syn::Member::Named(ident)
32                     } else {
33                         syn::Member::Unnamed(syn::Index {
34                             index: i as u32,
35                             span: field.span(),
36                         })
37                     };
38                     return Ok(Some(SourceCode { source_code }));
39                 }
40             }
41         }
42         Ok(None)
43     }
44 
gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream>45     pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
46         let (display_pat, _display_members) = display_pat_members(fields);
47         let src = &self.source_code;
48         Some(quote! {
49             #[allow(unused_variables)]
50             fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> {
51                 let Self #display_pat = self;
52                 Some(&self.#src)
53             }
54         })
55     }
56 
gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream>57     pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
58         gen_all_variants_with(
59             variants,
60             WhichFn::SourceCode,
61             |ident, fields, DiagnosticConcreteArgs { source_code, .. }| {
62                 let (display_pat, _display_members) = display_pat_members(fields);
63                 source_code.as_ref().and_then(|source_code| {
64                     let field = match &source_code.source_code {
65                         syn::Member::Named(ident) => ident.clone(),
66                         syn::Member::Unnamed(syn::Index { index, .. }) => {
67                             format_ident!("_{}", index)
68                         }
69                     };
70                     let variant_name = ident.clone();
71                     match &fields {
72                         syn::Fields::Unit => None,
73                         _ => Some(quote! {
74                             Self::#variant_name #display_pat => std::option::Option::Some(#field),
75                         }),
76                     }
77                 })
78             },
79         )
80     }
81 }
82