Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EnumIs: Allow custom variant function names #336

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion strum_macros/src/helpers/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub mod kw {

// enum discriminant metadata
custom_keyword!(derive);
custom_keyword!(name);
custom_keyword!(vis);

// variant metadata
Expand All @@ -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 {
Expand Down Expand Up @@ -178,6 +180,10 @@ pub enum VariantMeta {
kw: kw::props,
props: Vec<(LitStr, LitStr)>,
},
Name {
kw: kw::name,
value: LitStr,
},
}

impl Parse for VariantMeta {
Expand Down Expand Up @@ -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())
}
Expand Down
11 changes: 11 additions & 0 deletions strum_macros/src/helpers/variant_props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct StrumVariantProperties {
pub string_props: Vec<(LitStr, LitStr)>,
serialize: Vec<LitStr>,
pub to_string: Option<LitStr>,
pub name: Option<LitStr>,
ident: Option<Ident>,
}

Expand Down Expand Up @@ -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 } => {
Expand Down Expand Up @@ -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);
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion strum_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
26 changes: 16 additions & 10 deletions strum_macros/src/macros/enum_is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,30 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
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
}
}
Expand All @@ -42,6 +49,5 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
impl #impl_generics #enum_name #ty_generics #where_clause {
#(#variants)*
}
}
.into())
})
}
8 changes: 8 additions & 0 deletions strum_tests/tests/enum_is.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_names)]
use std::borrow::Cow;
use strum::EnumIs;

Expand All @@ -22,6 +23,8 @@ enum Foo {
Unnamed1(Option<u128>),
Unnamed2(bool, u8),
MultiWordName,
#[strum(name = "aliased_name")]
CustomName,
#[strum(disabled)]
#[allow(dead_code)]
Disabled,
Expand Down Expand Up @@ -84,6 +87,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());
Expand Down
8 changes: 8 additions & 0 deletions strum_tests/tests/enum_variant_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Option<u8>> = ColorTable::filled(None);
Expand Down