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