From 640c0bb713a7417e310aff9e7fe8349814699e5b Mon Sep 17 00:00:00 2001 From: Evgeniy Dushistov Date: Mon, 29 Jul 2019 18:26:29 +0300 Subject: [PATCH] Implement serialize_all=UPPERCASE (#48) * Implement serialize_all=UPPERCASE * applying rustfmt 1.2.2-stable * handling of all options listed in serde docs for compatibility See https://serde.rs/container-attrs.html#container-attributes --- strum_macros/src/case_style.rs | 19 ++++- strum_macros/src/helpers.rs | 22 +++++- strum_macros/src/lib.rs | 122 +++++++++++++++++++++++------ strum_tests/tests/serialize_all.rs | 37 +++++++++ 4 files changed, 170 insertions(+), 30 deletions(-) create mode 100644 strum_tests/tests/serialize_all.rs diff --git a/strum_macros/src/case_style.rs b/strum_macros/src/case_style.rs index 17acb17c..afdb2fa8 100644 --- a/strum_macros/src/case_style.rs +++ b/strum_macros/src/case_style.rs @@ -6,17 +6,27 @@ pub enum CaseStyle { ShoutySnakeCase, SnakeCase, TitleCase, + UpperCase, + LowerCase, + ScreamingKebabCase, + PascalCase, } impl<'s> From<&'s str> for CaseStyle { fn from(text: &'s str) -> CaseStyle { match text { - "camel_case" => CaseStyle::CamelCase, - "kebab_case" => CaseStyle::KebabCase, + "lowercase" => CaseStyle::LowerCase, + "camel_case" | "PascalCase" => CaseStyle::PascalCase, + "kebab_case" | "kebab-case" => CaseStyle::KebabCase, "mixed_case" => CaseStyle::MixedCase, - "shouty_snake_case" | "shouty_snek_case" => CaseStyle::ShoutySnakeCase, + "shouty_snake_case" | "shouty_snek_case" | "SCREAMING_SNAKE_CASE" => { + CaseStyle::ShoutySnakeCase + } "snake_case" | "snek_case" => CaseStyle::SnakeCase, "title_case" => CaseStyle::TitleCase, + "UPPERCASE" => CaseStyle::UpperCase, + "camelCase" => CaseStyle::CamelCase, + "SCREAMING-KEBAB-CASE" => CaseStyle::ScreamingKebabCase, _ => panic!( "Unexpected case style for serialize_all: `{}`. Valid values are: `{:?}`", text, @@ -26,7 +36,8 @@ impl<'s> From<&'s str> for CaseStyle { "mixed_case", "shouty_snake_case", "snake_case", - "title_case" + "title_case", + "UPPERCASE", ] ), } diff --git a/strum_macros/src/helpers.rs b/strum_macros/src/helpers.rs index 2ce74a8a..dcec9567 100644 --- a/strum_macros/src/helpers.rs +++ b/strum_macros/src/helpers.rs @@ -149,14 +149,34 @@ pub fn convert_case(ident: &Ident, case_style: Option) -> String { let ident_string = ident.to_string(); if let Some(case_style) = case_style { match case_style { - CaseStyle::CamelCase => ident_string.to_camel_case(), + CaseStyle::PascalCase => ident_string.to_camel_case(), CaseStyle::KebabCase => ident_string.to_kebab_case(), CaseStyle::MixedCase => ident_string.to_mixed_case(), CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(), CaseStyle::SnakeCase => ident_string.to_snake_case(), CaseStyle::TitleCase => ident_string.to_title_case(), + CaseStyle::UpperCase => ident_string.to_uppercase(), + CaseStyle::LowerCase => ident_string.to_lowercase(), + CaseStyle::ScreamingKebabCase => ident_string.to_kebab_case().to_uppercase(), + CaseStyle::CamelCase => { + let camel_case = ident_string.to_camel_case(); + let mut pascal = String::with_capacity(camel_case.len()); + let mut it = camel_case.chars(); + if let Some(ch) = it.next() { + pascal.extend(ch.to_lowercase()); + } + pascal.extend(it); + pascal + } } } else { ident_string } } + +#[test] +fn test_convert_case() { + let id = Ident::new("test_me", proc_macro2::Span::call_site()); + assert_eq!("testMe", convert_case(&id, Some(CaseStyle::CamelCase))); + assert_eq!("TestMe", convert_case(&id, Some(CaseStyle::PascalCase))); +} diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs index 9abd193f..4634b9ad 100644 --- a/strum_macros/src/lib.rs +++ b/strum_macros/src/lib.rs @@ -22,10 +22,10 @@ mod case_style; mod display; mod enum_count; mod enum_discriminants; -mod enum_variant_names; mod enum_iter; mod enum_messages; mod enum_properties; +mod enum_variant_names; mod from_string; mod helpers; mod to_string; @@ -46,8 +46,14 @@ fn debug_print_generated(ast: &syn::DeriveInput, toks: &TokenStream) { } } -#[cfg_attr(not(feature = "verbose-enumstring-name"), proc_macro_derive(EnumString, attributes(strum)))] -#[cfg_attr(feature = "verbose-enumstring-name", proc_macro_derive(StrumEnumString, attributes(strum)))] +#[cfg_attr( + not(feature = "verbose-enumstring-name"), + proc_macro_derive(EnumString, attributes(strum)) +)] +#[cfg_attr( + feature = "verbose-enumstring-name", + proc_macro_derive(StrumEnumString, attributes(strum)) +)] pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -56,8 +62,14 @@ pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { toks.into() } -#[cfg_attr(not(feature = "verbose-asrefstr-name"), proc_macro_derive(AsRefStr, attributes(strum)))] -#[cfg_attr(feature = "verbose-asrefstr-name", proc_macro_derive(StrumAsRefStr, attributes(strum)))] +#[cfg_attr( + not(feature = "verbose-asrefstr-name"), + proc_macro_derive(AsRefStr, attributes(strum)) +)] +#[cfg_attr( + feature = "verbose-asrefstr-name", + proc_macro_derive(StrumAsRefStr, attributes(strum)) +)] pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -66,8 +78,14 @@ pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { toks.into() } -#[cfg_attr(not(feature = "verbose-variant-names"), proc_macro_derive(EnumVariantNames, attributes(strum)))] -#[cfg_attr(feature = "verbose-variant-names", proc_macro_derive(StrumEnumVariantNames, attributes(strum)))] +#[cfg_attr( + not(feature = "verbose-variant-names"), + proc_macro_derive(EnumVariantNames, attributes(strum)) +)] +#[cfg_attr( + feature = "verbose-variant-names", + proc_macro_derive(StrumEnumVariantNames, attributes(strum)) +)] pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -76,8 +94,14 @@ pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream toks.into() } -#[cfg_attr(feature = "verbose-asstaticstr-name", proc_macro_derive(StrumAsStaticStr, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-asstaticstr-name"), proc_macro_derive(AsStaticStr, attributes(strum)))] +#[cfg_attr( + feature = "verbose-asstaticstr-name", + proc_macro_derive(StrumAsStaticStr, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-asstaticstr-name"), + proc_macro_derive(AsStaticStr, attributes(strum)) +)] pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -86,8 +110,14 @@ pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream toks.into() } -#[cfg_attr(feature = "verbose-intostaticstr-name", proc_macro_derive(StrumIntoStaticStr, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-intostaticstr-name"), proc_macro_derive(IntoStaticStr, attributes(strum)))] +#[cfg_attr( + feature = "verbose-intostaticstr-name", + proc_macro_derive(StrumIntoStaticStr, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-intostaticstr-name"), + proc_macro_derive(IntoStaticStr, attributes(strum)) +)] pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -96,8 +126,14 @@ pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStrea toks.into() } -#[cfg_attr(feature = "verbose-tostring-name", proc_macro_derive(StrumToString, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-tostring-name"), proc_macro_derive(ToString, attributes(strum)))] +#[cfg_attr( + feature = "verbose-tostring-name", + proc_macro_derive(StrumToString, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-tostring-name"), + proc_macro_derive(ToString, attributes(strum)) +)] pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -106,8 +142,14 @@ pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { toks.into() } -#[cfg_attr(feature = "verbose-display-name", proc_macro_derive(StrumDisplay, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-display-name"), proc_macro_derive(Display, attributes(strum)))] +#[cfg_attr( + feature = "verbose-display-name", + proc_macro_derive(StrumDisplay, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-display-name"), + proc_macro_derive(Display, attributes(strum)) +)] pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -116,8 +158,14 @@ pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { toks.into() } -#[cfg_attr(feature = "verbose-enumiter-name", proc_macro_derive(StrumEnumIter, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-enumiter-name"), proc_macro_derive(EnumIter, attributes(strum)))] +#[cfg_attr( + feature = "verbose-enumiter-name", + proc_macro_derive(StrumEnumIter, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-enumiter-name"), + proc_macro_derive(EnumIter, attributes(strum)) +)] pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -126,8 +174,14 @@ pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { toks.into() } -#[cfg_attr(feature = "verbose-enummessage-name", proc_macro_derive(StrumEnumMessage, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-enummessage-name"), proc_macro_derive(EnumMessage, attributes(strum)))] +#[cfg_attr( + feature = "verbose-enummessage-name", + proc_macro_derive(StrumEnumMessage, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-enummessage-name"), + proc_macro_derive(EnumMessage, attributes(strum)) +)] pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -136,8 +190,14 @@ pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream toks.into() } -#[cfg_attr(feature = "verbose-enumproperty-name", proc_macro_derive(StrumEnumProperty, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-enumproperty-name"), proc_macro_derive(EnumProperty, attributes(strum)))] +#[cfg_attr( + feature = "verbose-enumproperty-name", + proc_macro_derive(StrumEnumProperty, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-enumproperty-name"), + proc_macro_derive(EnumProperty, attributes(strum)) +)] pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -146,8 +206,14 @@ pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStrea toks.into() } -#[cfg_attr(feature = "verbose-enumdiscriminants-name", proc_macro_derive(StrumEnumDiscriminants, attributes(strum, strum_discriminants)))] -#[cfg_attr(not(feature = "verbose-enumdiscriminants-name"), proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants)))] +#[cfg_attr( + feature = "verbose-enumdiscriminants-name", + proc_macro_derive(StrumEnumDiscriminants, attributes(strum, strum_discriminants)) +)] +#[cfg_attr( + not(feature = "verbose-enumdiscriminants-name"), + proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants)) +)] pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); @@ -156,8 +222,14 @@ pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenSt toks.into() } -#[cfg_attr(feature = "verbose-enumcount-name", proc_macro_derive(StrumEnumCount, attributes(strum)))] -#[cfg_attr(not(feature = "verbose-enumcount-name"), proc_macro_derive(EnumCount, attributes(strum)))] +#[cfg_attr( + feature = "verbose-enumcount-name", + proc_macro_derive(StrumEnumCount, attributes(strum)) +)] +#[cfg_attr( + not(feature = "verbose-enumcount-name"), + proc_macro_derive(EnumCount, attributes(strum)) +)] pub fn enum_count(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_count::enum_count_inner(&ast); diff --git a/strum_tests/tests/serialize_all.rs b/strum_tests/tests/serialize_all.rs new file mode 100644 index 00000000..c951a934 --- /dev/null +++ b/strum_tests/tests/serialize_all.rs @@ -0,0 +1,37 @@ +/// test serialize_all cooperation with other macroses +extern crate strum; +#[macro_use] +extern crate strum_macros; + +use std::str::FromStr; +use std::string::ToString; + +#[derive(Debug, Eq, PartialEq, EnumString, ToString, IntoStaticStr)] +#[strum(serialize_all = "title_case")] +enum Foo1 { + DarkBlack, + Dim { glow: usize }, + BrightWhite, +} + +#[test] +fn test_serialize_all_title_case() { + assert_eq!("Dark Black", Foo1::DarkBlack.to_string()); + assert_eq!(Foo1::DarkBlack, Foo1::from_str("Dark Black").unwrap()); + assert_eq!("Dark Black", <&'static str>::from(Foo1::DarkBlack)); +} + +#[derive(Debug, Eq, PartialEq, EnumString, ToString, IntoStaticStr)] +#[strum(serialize_all = "UPPERCASE")] +enum Foo2 { + DarkBlack, + Dim { glow: usize }, + BrightWhite, +} + +#[test] +fn test_serialize_all_upper_case() { + assert_eq!("DARKBLACK", Foo2::DarkBlack.to_string()); + assert_eq!(Foo2::DarkBlack, Foo2::from_str("DARKBLACK").unwrap()); + assert_eq!("DARKBLACK", <&'static str>::from(Foo2::DarkBlack)); +}