Support multiple #[related] in derive macro

This commit is contained in:
Zakarum 2024-05-08 19:04:50 +02:00
parent b82cc81b8e
commit 9a8fd166da
1 changed files with 30 additions and 10 deletions

View File

@ -8,7 +8,7 @@ use crate::{
utils::{display_pat_members, gen_all_variants_with}, utils::{display_pat_members, gen_all_variants_with},
}; };
pub struct Related(syn::Member); pub struct Related(Vec<syn::Member>);
impl Related { impl Related {
pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> { pub(crate) fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
@ -22,6 +22,7 @@ impl Related {
} }
fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> { fn from_fields_vec(fields: Vec<&syn::Field>) -> syn::Result<Option<Self>> {
let mut members = Vec::new();
for (i, field) in fields.iter().enumerate() { for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs { for attr in &field.attrs {
if attr.path().is_ident("related") { if attr.path().is_ident("related") {
@ -33,11 +34,17 @@ impl Related {
span: field.span(), span: field.span(),
}) })
}; };
return Ok(Some(Related(related)));
members.push(related);
} }
} }
} }
Ok(None)
if members.is_empty() {
return Ok(None);
}
Ok(Some(Self(members)))
} }
pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> { pub(crate) fn gen_enum(variants: &[DiagnosticDef]) -> Option<TokenStream> {
@ -47,16 +54,24 @@ impl Related {
|ident, fields, DiagnosticConcreteArgs { related, .. }| { |ident, fields, DiagnosticConcreteArgs { related, .. }| {
let (display_pat, _display_members) = display_pat_members(fields); let (display_pat, _display_members) = display_pat_members(fields);
related.as_ref().map(|related| { related.as_ref().map(|related| {
let rel = match &related.0 { assert_ne!(related.0.len(), 0);
syn::Member::Named(ident) => ident.clone(),
syn::Member::Unnamed(syn::Index { index, .. }) => { let mut rels = related.0.iter().map(|rel| {
format_ident!("_{}", index) match rel {
syn::Member::Named(ident) => ident.clone(),
syn::Member::Unnamed(syn::Index { index, .. }) => {
format_ident!("_{}", index)
}
} }
}; });
let rel = rels.next().unwrap();
quote! { quote! {
Self::#ident #display_pat => { Self::#ident #display_pat => {
std::option::Option::Some(std::boxed::Box::new( std::option::Option::Some(std::boxed::Box::new(
#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x }) #rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })
#(.chain(#rels.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })))*
)) ))
} }
} }
@ -66,12 +81,17 @@ impl Related {
} }
pub(crate) fn gen_struct(&self) -> Option<TokenStream> { pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
let rel = &self.0; assert_ne!(self.0.len(), 0);
let rel = &self.0[0];
let rels = &self.0[1..];
Some(quote! { Some(quote! {
fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> { fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
use ::core::borrow::Borrow; use ::core::borrow::Borrow;
std::option::Option::Some(std::boxed::Box::new( std::option::Option::Some(std::boxed::Box::new(
self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() }) self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })
#(.chain(self.#rels.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })))*
)) ))
} }
}) })