diff --git a/strum_macros/src/helpers/case_style.rs b/strum_macros/src/helpers/case_style.rs index 86a85830..bcea7886 100644 --- a/strum_macros/src/helpers/case_style.rs +++ b/strum_macros/src/helpers/case_style.rs @@ -1,5 +1,6 @@ use heck::{ - ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToUpperCamelCase, ToTrainCase, + ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToTrainCase, + ToUpperCamelCase, }; use std::str::FromStr; use syn::{ diff --git a/strum_macros/src/helpers/metadata.rs b/strum_macros/src/helpers/metadata.rs index d638ae3c..19eb9270 100644 --- a/strum_macros/src/helpers/metadata.rs +++ b/strum_macros/src/helpers/metadata.rs @@ -17,6 +17,7 @@ pub mod kw { // enum metadata custom_keyword!(serialize_all); custom_keyword!(use_phf); + custom_keyword!(prefix); // enum discriminant metadata custom_keyword!(derive); @@ -45,6 +46,10 @@ pub enum EnumMeta { crate_module_path: Path, }, UsePhf(kw::use_phf), + Prefix { + kw: kw::prefix, + prefix: LitStr, + }, } impl Parse for EnumMeta { @@ -69,6 +74,11 @@ impl Parse for EnumMeta { Ok(EnumMeta::AsciiCaseInsensitive(input.parse()?)) } else if lookahead.peek(kw::use_phf) { Ok(EnumMeta::UsePhf(input.parse()?)) + } else if lookahead.peek(kw::prefix) { + let kw = input.parse::()?; + input.parse::()?; + let prefix = input.parse()?; + Ok(EnumMeta::Prefix { kw, prefix }) } else { Err(lookahead.error()) } diff --git a/strum_macros/src/helpers/mod.rs b/strum_macros/src/helpers/mod.rs index 142ea0b8..724fce2f 100644 --- a/strum_macros/src/helpers/mod.rs +++ b/strum_macros/src/helpers/mod.rs @@ -1,4 +1,4 @@ -pub use self::case_style::{CaseStyleHelpers, snakify}; +pub use self::case_style::{snakify, CaseStyleHelpers}; pub use self::type_props::HasTypeProperties; pub use self::variant_props::HasStrumVariantProperties; diff --git a/strum_macros/src/helpers/type_props.rs b/strum_macros/src/helpers/type_props.rs index 17d84c98..7302853e 100644 --- a/strum_macros/src/helpers/type_props.rs +++ b/strum_macros/src/helpers/type_props.rs @@ -1,7 +1,7 @@ use proc_macro2::TokenStream; use quote::quote; use std::default::Default; -use syn::{parse_quote, DeriveInput, Ident, Path, Visibility}; +use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility}; use super::case_style::CaseStyle; use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta}; @@ -21,6 +21,7 @@ pub struct StrumTypeProperties { pub discriminant_others: Vec, pub discriminant_vis: Option, pub use_phf: bool, + pub prefix: Option, pub enum_repr: Option, } @@ -35,6 +36,7 @@ impl HasTypeProperties for DeriveInput { let mut ascii_case_insensitive_kw = None; let mut use_phf_kw = None; let mut crate_module_path_kw = None; + let mut prefix_kw = None; for meta in strum_meta { match meta { EnumMeta::SerializeAll { case_style, kw } => { @@ -72,6 +74,14 @@ impl HasTypeProperties for DeriveInput { crate_module_path_kw = Some(kw); output.crate_module_path = Some(crate_module_path); } + EnumMeta::Prefix { prefix, kw } => { + if let Some(fst_kw) = prefix_kw { + return Err(occurrence_error(fst_kw, kw, "prefix")); + } + + prefix_kw = Some(kw); + output.prefix = Some(prefix); + } } } diff --git a/strum_macros/src/macros/enum_messages.rs b/strum_macros/src/macros/enum_messages.rs index c0561085..2dad4117 100644 --- a/strum_macros/src/macros/enum_messages.rs +++ b/strum_macros/src/macros/enum_messages.rs @@ -74,14 +74,17 @@ pub fn enum_message_inner(ast: &DeriveInput) -> syn::Result { if !documentation.is_empty() { let params = params.clone(); // Strip a single leading space from each documentation line. - let documentation: Vec = documentation.iter().map(|lit_str| { - let line = lit_str.value(); - if line.starts_with(' ') { - LitStr::new(&line.as_str()[1..], lit_str.span()) - } else { - lit_str.clone() - } - }).collect(); + let documentation: Vec = documentation + .iter() + .map(|lit_str| { + let line = lit_str.value(); + if line.starts_with(' ') { + LitStr::new(&line.as_str()[1..], lit_str.span()) + } else { + lit_str.clone() + } + }) + .collect(); if documentation.len() == 1 { let text = &documentation[0]; documentation_arms diff --git a/strum_macros/src/macros/enum_variant_names.rs b/strum_macros/src/macros/enum_variant_names.rs index c54d45dc..f53b0afa 100644 --- a/strum_macros/src/macros/enum_variant_names.rs +++ b/strum_macros/src/macros/enum_variant_names.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DeriveInput}; +use syn::{Data, DeriveInput, LitStr}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -24,7 +24,7 @@ pub fn enum_variant_names_inner(ast: &DeriveInput) -> syn::Result { let props = v.get_variant_properties()?; Ok(props.get_preferred_name(type_properties.case_style)) }) - .collect::>>()?; + .collect::>>()?; Ok(quote! { impl #impl_generics #strum_module_path::VariantNames for #name #ty_generics #where_clause { diff --git a/strum_macros/src/macros/strings/display.rs b/strum_macros/src/macros/strings/display.rs index 77f8afa1..e6a2459c 100644 --- a/strum_macros/src/macros/strings/display.rs +++ b/strum_macros/src/macros/strings/display.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DeriveInput, Fields}; +use syn::{Data, DeriveInput, Fields, LitStr}; use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; @@ -24,7 +24,10 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result { } // Look at all the serialize attributes. - let output = variant_properties.get_preferred_name(type_properties.case_style); + let mut output = variant_properties.get_preferred_name(type_properties.case_style); + if let Some(prefix) = &type_properties.prefix { + output = LitStr::new(&(prefix.value() + &output.value()), output.span()); + } let params = match variant.fields { Fields::Unit => quote! {}, diff --git a/strum_macros/src/macros/strings/from_string.rs b/strum_macros/src/macros/strings/from_string.rs index 2d255917..4b9a7219 100644 --- a/strum_macros/src/macros/strings/from_string.rs +++ b/strum_macros/src/macros/strings/from_string.rs @@ -79,7 +79,7 @@ pub fn from_string_inner(ast: &DeriveInput) -> syn::Result { phf_exact_match_arms.push(quote! { #serialization => #name::#ident #params, }); if is_ascii_case_insensitive { - // Store the lowercase and UPPERCASE variants in the phf map to capture + // Store the lowercase and UPPERCASE variants in the phf map to capture let ser_string = serialization.value(); let lower = diff --git a/strum_tests/tests/enum_is.rs b/strum_tests/tests/enum_is.rs index 4e997fea..3226ebbc 100644 --- a/strum_tests/tests/enum_is.rs +++ b/strum_tests/tests/enum_is.rs @@ -11,8 +11,13 @@ enum LifeTimeTest<'a>{ enum Foo { Unit, Named0 {}, - Named1 { _a: char }, - Named2 { _a: u32, _b: String }, + Named1 { + _a: char, + }, + Named2 { + _a: u32, + _b: String, + }, Unnamed0(), Unnamed1(Option), Unnamed2(bool, u8), diff --git a/strum_tests/tests/enum_message.rs b/strum_tests/tests/enum_message.rs index b1c86004..e7ff3033 100644 --- a/strum_tests/tests/enum_message.rs +++ b/strum_tests/tests/enum_message.rs @@ -49,7 +49,10 @@ fn only_detailed_message() { #[test] fn documentation() { - assert_eq!("I eat birds.\n\nAnd fish.\n", (Pets::Cat).get_documentation().unwrap()); + assert_eq!( + "I eat birds.\n\nAnd fish.\n", + (Pets::Cat).get_documentation().unwrap() + ); assert_eq!("I'm a fish.", (Pets::Fish).get_documentation().unwrap()); assert_eq!("I'm a bird.", (Pets::Bird).get_documentation().unwrap()); } diff --git a/strum_tests/tests/enum_try_as.rs b/strum_tests/tests/enum_try_as.rs index aa54205e..6cbf81e5 100644 --- a/strum_tests/tests/enum_try_as.rs +++ b/strum_tests/tests/enum_try_as.rs @@ -11,7 +11,10 @@ enum Foo { #[allow(dead_code)] Unit, #[allow(dead_code)] - Named { _a: u32, _b: String }, + Named { + _a: u32, + _b: String, + }, } #[test] diff --git a/strum_tests/tests/prefix.rs b/strum_tests/tests/prefix.rs new file mode 100644 index 00000000..806b5d52 --- /dev/null +++ b/strum_tests/tests/prefix.rs @@ -0,0 +1,20 @@ +use strum::{Display, EnumString}; + +#[allow(dead_code)] +#[derive(Debug, EnumString, Display)] +#[strum(prefix = "colour/")] +enum Color { + #[strum(to_string = "RedRed")] + Red, + #[strum(serialize = "b", to_string = "blue")] + Blue { hue: usize }, + #[strum(serialize = "y", serialize = "yellow")] + Yellow, + #[strum(default)] + Green(String), +} + +#[test] +fn prefix_redred() { + assert_eq!(String::from("colour/RedRed"), (Color::Red).to_string()); +}