diff --git a/strum_macros/src/enum_discriminants.rs b/strum_macros/src/enum_discriminants.rs index ab35a9b9..1877c4ad 100644 --- a/strum_macros/src/enum_discriminants.rs +++ b/strum_macros/src/enum_discriminants.rs @@ -1,7 +1,9 @@ use proc_macro2::{Span, TokenStream}; use syn; -use helpers::{extract_list_metas, extract_meta, get_meta_ident, get_meta_list, unique_meta_list}; +use helpers::{ + extract_list_metas, extract_meta, filter_metas, get_meta_ident, get_meta_list, unique_meta_list, +}; pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; @@ -37,6 +39,14 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream { .and_then(|metas| metas.filter_map(get_meta_ident).next()) .unwrap_or(&default_name); + // Pass through all other attributes + let pass_though_attributes = + filter_metas(discriminant_attrs.iter().map(|&m| m), |meta| match meta { + syn::Meta::List(ref metalist) => metalist.ident != "derive" && metalist.ident != "name", + _ => true, + }).map(|meta| quote! { #[ #meta ] }) + .collect::>(); + // Add the variants without fields, but exclude the `strum` meta item let mut discriminants = Vec::new(); for variant in variants { @@ -56,6 +66,7 @@ pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream { quote!{ /// Auto-generated discriminant enum variants #derives + #(#pass_though_attributes)* #vis enum #discriminants_name { #(#discriminants),* } diff --git a/strum_macros/src/helpers.rs b/strum_macros/src/helpers.rs index 306b34cc..eb485861 100644 --- a/strum_macros/src/helpers.rs +++ b/strum_macros/src/helpers.rs @@ -10,6 +10,34 @@ pub fn extract_meta(attrs: &[Attribute]) -> Vec { .collect() } +pub fn filter_metas<'meta, MetaIt, F>(metas: MetaIt, filter: F) -> impl Iterator +where + MetaIt: Iterator, + F: Fn(&Meta) -> bool, +{ + metas.filter_map(move |meta| if filter(meta) { Some(meta) } else { None }) +} + +pub fn filter_meta_lists<'meta, MetaIt, F>( + metas: MetaIt, + filter: F, +) -> impl Iterator +where + MetaIt: Iterator, + F: Fn(&MetaList) -> bool, +{ + metas.filter_map(move |meta| match meta { + Meta::List(ref metalist) => { + if filter(metalist) { + Some(metalist) + } else { + None + } + } + _ => None, + }) +} + /// Returns the `MetaList`s with the given attr name. /// /// For example, `get_meta_list(type_meta.iter(), "strum_discriminant")` for the following snippet @@ -29,18 +57,7 @@ pub fn get_meta_list<'meta, MetaIt>( where MetaIt: Iterator, { - metas - // Get all the attributes with our tag on them. - .filter_map(move |meta| match meta { - Meta::List(ref metalist) => { - if metalist.ident == attr { - Some(metalist) - } else { - None - } - } - _ => None, - }) + filter_meta_lists(metas, move |metalist| metalist.ident == attr) } pub fn unique_meta_list<'meta, MetaIt>(metas: MetaIt, attr: &'meta str) -> Option<&'meta MetaList> diff --git a/strum_tests/tests/enum_discriminants.rs b/strum_tests/tests/enum_discriminants.rs index 18900277..db5b957a 100644 --- a/strum_tests/tests/enum_discriminants.rs +++ b/strum_tests/tests/enum_discriminants.rs @@ -118,3 +118,30 @@ fn split_attributes_test() { assert_eq!(expected, discriminants); assert_eq!("Variant0", format!("{}", SplitAttributesBoo::Variant0)); } + +#[allow(dead_code)] +#[derive(Debug, Eq, PartialEq, EnumDiscriminants)] +#[strum_discriminants( + name(PassThroughBoo), + derive(Display, EnumIter, EnumString), + strum(serialize_all = "snake_case"), +)] +enum PassThrough { + DarkBlack(bool), + BrightWhite(i32), +} + +#[test] +fn arbitrary_attributes_pass_through() { + use std::str::FromStr; + + let discriminants = PassThroughBoo::iter().collect::>(); + let expected = vec![PassThroughBoo::DarkBlack, PassThroughBoo::BrightWhite]; + + assert_eq!(expected, discriminants); + assert_eq!("dark_black", PassThroughBoo::DarkBlack.to_string()); + assert_eq!( + PassThroughBoo::DarkBlack, + PassThroughBoo::from_str("dark_black").unwrap() + ); +}