From e6a9bf08c4ca009ae46fde46efed59572bc9fd42 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sat, 17 Jun 2023 17:58:12 -0700 Subject: [PATCH] fix!: use the tuple value for to_string on default (#270) The default attribute on a tuple like variant now causes the to_string and display format to use the value of the tuple rather than the name of the variant. E.g. Color::Green("lime").to_string() will equal "lime" not "Green" Fixes: how to round trip Display and EnumString with default="true" #86 BREAKING CHANGE This changes how Display and ToString cause the following to renders: ```rust #[strum(default)] Green(String) ``` To maintain the previous behavior (use the variant name): ```rust #[strum(default, to_string("Green"))] Green(String) ``` --- strum_macros/src/helpers/variant_props.rs | 2 +- strum_macros/src/macros/strings/display.rs | 16 ++++++++++- strum_macros/src/macros/strings/to_string.rs | 16 +++++++++++ strum_tests/tests/display.rs | 22 ++++++++++++++ strum_tests/tests/to_string.rs | 30 ++++++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/strum_macros/src/helpers/variant_props.rs b/strum_macros/src/helpers/variant_props.rs index a39d3ea1..f6372532 100644 --- a/strum_macros/src/helpers/variant_props.rs +++ b/strum_macros/src/helpers/variant_props.rs @@ -19,7 +19,7 @@ pub struct StrumVariantProperties { pub documentation: Vec, pub string_props: Vec<(LitStr, LitStr)>, serialize: Vec, - to_string: Option, + pub to_string: Option, ident: Option, } diff --git a/strum_macros/src/macros/strings/display.rs b/strum_macros/src/macros/strings/display.rs index 82a34b0d..fcc5936a 100644 --- a/strum_macros/src/macros/strings/display.rs +++ b/strum_macros/src/macros/strings/display.rs @@ -32,7 +32,21 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result { Fields::Named(..) => quote! { {..} }, }; - arms.push(quote! { #name::#ident #params => f.pad(#output) }); + if variant_properties.to_string.is_none() && variant_properties.default.is_some() { + match &variant.fields { + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + arms.push(quote! { #name::#ident(ref s) => f.pad(s) }); + } + _ => { + return Err(syn::Error::new_spanned( + variant, + "Default only works on newtype structs with a single String field", + )) + } + } + } else { + arms.push(quote! { #name::#ident #params => f.pad(#output) }); + } } if arms.len() < variants.len() { diff --git a/strum_macros/src/macros/strings/to_string.rs b/strum_macros/src/macros/strings/to_string.rs index 7e4c4838..9a1e6617 100644 --- a/strum_macros/src/macros/strings/to_string.rs +++ b/strum_macros/src/macros/strings/to_string.rs @@ -22,6 +22,22 @@ pub fn to_string_inner(ast: &DeriveInput) -> syn::Result { continue; } + // display variants like Green("lime") as "lime" + if variant_properties.to_string.is_none() && variant_properties.default.is_some() { + match &variant.fields { + Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + arms.push(quote! { #name::#ident(ref s) => ::std::string::String::from(s) }); + continue; + } + _ => { + return Err(syn::Error::new_spanned( + variant, + "Default only works on newtype structs with a single String field", + )) + } + } + } + // Look at all the serialize attributes. let output = variant_properties.get_preferred_name(type_properties.case_style); diff --git a/strum_tests/tests/display.rs b/strum_tests/tests/display.rs index 2b20a56b..2e1b9968 100644 --- a/strum_tests/tests/display.rs +++ b/strum_tests/tests/display.rs @@ -44,6 +44,28 @@ fn to_red_string() { assert_eq!(String::from("RedRed"), format!("{}", Color::Red)); } +#[test] +fn to_green_string() { + assert_eq!( + String::from("lime"), + format!("{}", Color::Green("lime".into())) + ); +} + +#[derive(Debug, Eq, PartialEq, EnumString, Display)] +enum ColorWithDefaultAndToString { + #[strum(default, to_string = "GreenGreen")] + Green(String), +} + +#[test] +fn to_green_with_default_and_to_string() { + assert_eq!( + String::from("GreenGreen"), + format!("{}", ColorWithDefaultAndToString::Green("lime".into())) + ); +} + #[derive(Display, Debug, Eq, PartialEq)] #[strum(serialize_all = "snake_case")] enum Brightness { diff --git a/strum_tests/tests/to_string.rs b/strum_tests/tests/to_string.rs index 8b59a9e9..91538fc5 100644 --- a/strum_tests/tests/to_string.rs +++ b/strum_tests/tests/to_string.rs @@ -38,6 +38,36 @@ fn to_red_string() { ); } +#[test] +fn to_green_string_with_default() { + assert_eq!( + String::from("lime"), + (Color::Green("lime".into())).to_string() + ); + assert_eq!( + Color::Green("lime".into()), + Color::from_str("lime").unwrap() + ); +} + +#[derive(Debug, Eq, PartialEq, EnumString, ToString)] +enum ColorWithDefaultAndToString { + #[strum(default, to_string = "GreenGreen")] + Green(String), +} + +#[test] +fn to_green_with_default_and_to_string() { + assert_eq!( + String::from("GreenGreen"), + (ColorWithDefaultAndToString::Green("lime".into())).to_string() + ); + assert_eq!( + ColorWithDefaultAndToString::Green("lime".into()), + ColorWithDefaultAndToString::from_str("lime").unwrap() + ); +} + #[derive(Debug, Eq, PartialEq, ToString)] #[strum(serialize_all = "snake_case")] enum Brightness {