Repository: Peternator7/strum
Pull request #48 (2019-04-09 09:13:29)

Implement serialize_all=UPPERCASE
I suppose it would be great if we would be compatible with serde:
https://serde.rs/container-attrs.html

> "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case", "SCREAMING-KEBAB-CASE"

when it is clear from the name how text would be changed, like "camelCase" instead of "camel_case"



Dushistov: @Peternator7 

ping
Peternator7: Thanks for the PR! I think matching Serde's naming is reasonable, but if we do it for one, then we should do it for all of them and just take the breaking change. Is that something you could do?
Dushistov: @Peternator7 

> I think matching Serde's naming is reasonable,

Great!

> but if we do it for one, then we should do it for all of them and just take the breaking change. Is that something you could do?

I suppose there is no need for breaking changes, I don't see any intersections that have the same name for different cases. As first step we can just add new names:
```diff
- "camel_case" => CaseStyle::CamelCase,
+ "camel_case" | "camelCase" => CaseStyle::CamelCase,
```
in future may be there is sense to deprecate "camel_case" or may be not.


pitkley: @Dushistov are you still working on this PR? I have a use-case for the `lowercase` renamer and would be happy to push this PR further.
Dushistov: @pitkley 

> are you still working on this PR?

No, it just stuck on @Peternator7 's side,
in other words even if I plan to work on this I have no idea what to do to merge this PR.


Peternator7: Sorry I didn't realize this was blocked on me. I'd like to get all the different cases implemented in a single pr if we can.

- [ ] snake_case
- [ ] screaming_snake_case
- [ ] kebab_case
- [ ] screaming_kebab_case
- [ ] lowercase
- [ ] uppercase
- [ ] camel_case
- [ ] pascal_case

I agree with @Dushistov that we don't need a breaking change to implement. Let's just add these in addition to the currently accepted values.
Dushistov: > Let's just add these in addition to the currently accepted values

I add all from your list above (and from serde's doc, actually they are the same).
Little messy with 'camel_case', according to serde and google,
camel case starts with lower case, while pascal case starts with upper case,
but for compatibility I leave old name also,
so 'camel_case' and 'camelCase' are different.

Patch:
diff --git a/strum_macros/src/case_style.rs b/strum_macros/src/case_style.rs
index 17acb17..afdb2fa 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 2ce74a8..dcec956 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<CaseStyle>) -> 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 9abd193..4634b9a 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);


Test patch:
diff --git a/strum_tests/tests/serialize_all.rs b/strum_tests/tests/serialize_all.rs
new file mode 100644
index 0000000..c951a93
--- /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));
+}