use crate::helpers::call_site_ident; use proc_macro2::Ident; use proc_macro2::TokenStream; use quote::ToTokens; use syn::Token; use syn::parse::{Parse, ParseStream}; use syn::{ItemEnum, TypePath}; struct MessageArgs { pub _top_parent: TypePath, pub _comma1: Token![,], pub parent: TypePath, pub _comma2: Token![,], pub variant: Ident, } impl Parse for MessageArgs { fn parse(input: ParseStream) -> syn::Result { Ok(Self { _top_parent: input.parse()?, _comma1: input.parse()?, parent: input.parse()?, _comma2: input.parse()?, variant: input.parse()?, }) } } struct TopLevelMessageArgs { pub parent: TypePath, pub _comma2: Token![,], pub variant: Ident, } impl Parse for TopLevelMessageArgs { fn parse(input: ParseStream) -> syn::Result { Ok(Self { parent: input.parse()?, _comma2: input.parse()?, variant: input.parse()?, }) } } pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -> syn::Result { if attr.is_empty() { return top_level_impl(input_item); } let mut input = syn::parse2::(input_item)?; let (parent_is_top, parent, variant) = match syn::parse2::(attr.clone()) { Ok(x) => (false, x.parent, x.variant), Err(_) => { let x = syn::parse2::(attr)?; (true, x.parent, x.variant) } }; let parent_discriminant = quote::quote! { <#parent as ToDiscriminant>::Discriminant }; input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild)] }); input.attrs.push(syn::parse_quote! { #[parent(#parent, #parent::#variant)] }); if parent_is_top { input.attrs.push(syn::parse_quote! { #[parent_is_top] }); } input .attrs .push(syn::parse_quote! { #[discriminant_attr(derive(Debug, Copy, Clone, PartialEq, Eq, Hash, AsMessage, TransitiveChild))] }); input .attrs .push(syn::parse_quote! { #[discriminant_attr(parent(#parent_discriminant, #parent_discriminant::#variant))] }); if parent_is_top { input.attrs.push(syn::parse_quote! { #[discriminant_attr(parent_is_top)] }); } for var in &mut input.variants { if let Some(attr) = var.attrs.iter_mut().find(|a| a.path().is_ident("child")) { let path = match &mut attr.meta { syn::Meta::Path(path) => path, syn::Meta::List(list) => &mut list.path, syn::Meta::NameValue(named_value) => &mut named_value.path, }; let last_segment = path.segments.last_mut().unwrap(); last_segment.ident = call_site_ident("sub_discriminant"); var.attrs.push(syn::parse_quote! { #[discriminant_attr(child)] }); } } Ok(input.into_token_stream()) } fn top_level_impl(input_item: TokenStream) -> syn::Result { let mut input = syn::parse2::(input_item)?; input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant)] }); input.attrs.push(syn::parse_quote! { #[discriminant_attr(derive(Debug, Copy, Clone, PartialEq, Eq, Hash, AsMessage))] }); for var in &mut input.variants { if let Some(attr) = var.attrs.iter_mut().find(|a| a.path().is_ident("child")) { let path = match &mut attr.meta { syn::Meta::Path(path) => path, syn::Meta::List(list) => &mut list.path, syn::Meta::NameValue(named_value) => &mut named_value.path, }; let last_segment = path.segments.last_mut().unwrap(); last_segment.ident = call_site_ident("sub_discriminant"); var.attrs.push(syn::parse_quote! { #[discriminant_attr(child)] }); } } let input_type = &input.ident; let discriminant = call_site_ident(format!("{input_type}Discriminant")); Ok(quote::quote! { #input impl TransitiveChild for #input_type { type TopParent = Self; type Parent = Self; } impl TransitiveChild for #discriminant { type TopParent = Self; type Parent = Self; } }) }