From 3ee7036024b588985936b3eae0514ae7584326ed Mon Sep 17 00:00:00 2001 From: Magnus Bergmark Date: Wed, 28 Feb 2024 21:56:23 +0100 Subject: [PATCH 1/2] EnumIs: Allow custom variant function names --- strum_macros/src/helpers/metadata.rs | 13 +++++++++++- strum_macros/src/helpers/variant_props.rs | 11 ++++++++++ strum_macros/src/lib.rs | 6 +++++- strum_macros/src/macros/enum_is.rs | 26 ++++++++++++++--------- strum_tests/tests/enum_is.rs | 7 ++++++ 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/strum_macros/src/helpers/metadata.rs b/strum_macros/src/helpers/metadata.rs index 94100a7f..f788f376 100644 --- a/strum_macros/src/helpers/metadata.rs +++ b/strum_macros/src/helpers/metadata.rs @@ -21,7 +21,6 @@ pub mod kw { // enum discriminant metadata custom_keyword!(derive); - custom_keyword!(name); custom_keyword!(vis); // variant metadata @@ -34,6 +33,9 @@ pub mod kw { custom_keyword!(default_with); custom_keyword!(props); custom_keyword!(ascii_case_insensitive); + + // enum discriminant metadata and variant metadata + custom_keyword!(name); } pub enum EnumMeta { @@ -178,6 +180,10 @@ pub enum VariantMeta { kw: kw::props, props: Vec<(LitStr, LitStr)>, }, + Name { + kw: kw::name, + value: LitStr, + }, } impl Parse for VariantMeta { @@ -233,6 +239,11 @@ impl Parse for VariantMeta { .map(|Prop(k, v)| (LitStr::new(&k.to_string(), k.span()), v)) .collect(), }) + } else if lookahead.peek(kw::name) { + let kw = input.parse()?; + let _: Token![=] = input.parse()?; + let value = input.parse()?; + Ok(VariantMeta::Name { kw, value }) } else { Err(lookahead.error()) } diff --git a/strum_macros/src/helpers/variant_props.rs b/strum_macros/src/helpers/variant_props.rs index d6e0cab3..19ee85ad 100644 --- a/strum_macros/src/helpers/variant_props.rs +++ b/strum_macros/src/helpers/variant_props.rs @@ -21,6 +21,7 @@ pub struct StrumVariantProperties { pub string_props: Vec<(LitStr, LitStr)>, serialize: Vec, pub to_string: Option, + pub name: Option, ident: Option, } @@ -68,6 +69,8 @@ impl HasStrumVariantProperties for Variant { let mut default_with_kw = None; let mut to_string_kw = None; let mut ascii_case_insensitive_kw = None; + let mut name_kw = None; + for meta in self.get_metadata()? { match meta { VariantMeta::Message { value, kw } => { @@ -135,6 +138,14 @@ impl HasStrumVariantProperties for Variant { VariantMeta::Props { props, .. } => { output.string_props.extend(props); } + VariantMeta::Name { kw, value } => { + if let Some(fst_kw) = name_kw { + return Err(occurrence_error(fst_kw, kw, "name")); + } + + name_kw = Some(kw); + output.name = Some(value); + } } } diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs index 11a1cd7b..4069b40e 100644 --- a/strum_macros/src/lib.rs +++ b/strum_macros/src/lib.rs @@ -458,17 +458,21 @@ pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { /// E.g. `Color.is_red()`. /// /// ``` -/// /// use strum_macros::EnumIs; /// /// #[derive(EnumIs, Debug)] /// enum Color { /// Red, /// Green { range: usize }, +/// // Generated function names can be customized; default would be `is_rebecca_purple`, but +/// // this generates `is_rebeccapurple` to match the CSS color name. +/// #[strum(name = "rebeccapurple")] +/// RebeccaPurple, /// } /// /// assert!(Color::Red.is_red()); /// assert!(Color::Green{range: 0}.is_green()); +/// assert!(Color::RebeccaPurple.is_rebeccapurple()); /// ``` #[proc_macro_derive(EnumIs, attributes(strum))] pub fn enum_is(input: proc_macro::TokenStream) -> proc_macro::TokenStream { diff --git a/strum_macros/src/macros/enum_is.rs b/strum_macros/src/macros/enum_is.rs index c239628d..74c15c52 100644 --- a/strum_macros/src/macros/enum_is.rs +++ b/strum_macros/src/macros/enum_is.rs @@ -14,23 +14,30 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result { let variants: Vec<_> = variants .iter() .filter_map(|variant| { - if variant.get_variant_properties().ok()?.disabled.is_some() { + let variant_props = variant.get_variant_properties().ok()?; + + if variant_props.disabled.is_some() { return None; } - let variant_name = &variant.ident; - let fn_name = format_ident!("is_{}", snakify(&variant_name.to_string())); - let doc_comment = format!( - "Returns [true] if the enum is [{}::{}] otherwise [false]", - enum_name, variant_name - ); + let ident = &variant.ident; + let name = variant_props + .name + .as_ref() + .map(|s| s.value()) + .unwrap_or_else(|| snakify(&ident.to_string())); + + let fn_name = format_ident!("is_{}", name); + let doc_comment = + format!("Returns [true] if the enum is [{enum_name}::{ident}] otherwise [false]",); + Some(quote! { #[must_use] #[inline] #[doc = #doc_comment] pub const fn #fn_name(&self) -> bool { match self { - &#enum_name::#variant_name { .. } => true, + &#enum_name::#ident { .. } => true, _ => false } } @@ -42,6 +49,5 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result { impl #impl_generics #enum_name #ty_generics #where_clause { #(#variants)* } - } - .into()) + }) } diff --git a/strum_tests/tests/enum_is.rs b/strum_tests/tests/enum_is.rs index d45ae4cb..12a28fa7 100644 --- a/strum_tests/tests/enum_is.rs +++ b/strum_tests/tests/enum_is.rs @@ -22,6 +22,8 @@ enum Foo { Unnamed1(Option), Unnamed2(bool, u8), MultiWordName, + #[strum(name = "aliased_name")] + CustomName, #[strum(disabled)] #[allow(dead_code)] Disabled, @@ -84,6 +86,11 @@ fn multi_word() { assert!(Foo::MultiWordName.is_multi_word_name()); } +#[test] +fn aliased_name() { + assert!(Foo::CustomName.is_aliased_name()); +} + #[test] fn doesnt_match_other_variations() { assert!(!Foo::Unit.is_multi_word_name()); From 9f26f66ba3383b21f8e3cca17219a16d2e161a87 Mon Sep 17 00:00:00 2001 From: Magnus Bergmark Date: Wed, 28 Feb 2024 22:02:52 +0100 Subject: [PATCH 2/2] Fix some compilation warnings and cover table panic --- strum_tests/tests/enum_is.rs | 1 + strum_tests/tests/enum_variant_table.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/strum_tests/tests/enum_is.rs b/strum_tests/tests/enum_is.rs index 12a28fa7..30f363a6 100644 --- a/strum_tests/tests/enum_is.rs +++ b/strum_tests/tests/enum_is.rs @@ -1,3 +1,4 @@ +#![allow(clippy::disallowed_names)] use std::borrow::Cow; use strum::EnumIs; diff --git a/strum_tests/tests/enum_variant_table.rs b/strum_tests/tests/enum_variant_table.rs index 25e854fd..ba749ae6 100644 --- a/strum_tests/tests/enum_variant_table.rs +++ b/strum_tests/tests/enum_variant_table.rs @@ -14,6 +14,7 @@ enum Color { // even though this isn't used, it needs to be a test // because if it doesn't compile, enum variants that conflict with keywords won't work +#[allow(dead_code)] #[derive(EnumTable)] enum Keyword { Const, @@ -70,6 +71,13 @@ fn index_mut() { assert_eq!(map[Color::Red], 72); } +#[test] +#[should_panic] +fn index_disabled_panics() { + let map = ColorTable::filled(0); + println!("Returned: {}", map[Color::Teal]); +} + #[test] fn option_all() { let mut map: ColorTable> = ColorTable::filled(None);