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 Related(syn::Member);
12 
13 impl Related {
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("related") {
28                     let related = 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(Related(related)));
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::Related,
47             |ident, fields, DiagnosticConcreteArgs { related, .. }| {
48                 let (display_pat, _display_members) = display_pat_members(fields);
49                 related.as_ref().map(|related| {
50                     let rel = match &related.0 {
51                         syn::Member::Named(ident) => ident.clone(),
52                         syn::Member::Unnamed(syn::Index { index, .. }) => {
53                             format_ident!("_{}", index)
54                         }
55                     };
56                     quote! {
57                         Self::#ident #display_pat => {
58                             std::option::Option::Some(std::boxed::Box::new(
59                                 #rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })
60                             ))
61                         }
62                     }
63                 })
64             },
65         )
66     }
67 
gen_struct(&self) -> Option<TokenStream>68     pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
69         let rel = &self.0;
70         Some(quote! {
71             fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
72                 use ::core::borrow::Borrow;
73                 std::option::Option::Some(std::boxed::Box::new(
74                         self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })
75                 ))
76             }
77         })
78     }
79 }
80