diff --git a/core/src/codegen/from_meta_impl.rs b/core/src/codegen/from_meta_impl.rs index 5a65c5d..68bafef 100644 --- a/core/src/codegen/from_meta_impl.rs +++ b/core/src/codegen/from_meta_impl.rs @@ -77,7 +77,6 @@ impl<'a> ToTokens for FromMetaImpl<'a> { } Data::Enum(ref variants) => { let unit_arms = variants.iter().map(Variant::as_unit_match_arm); - let struct_arms = variants.iter().map(Variant::as_data_match_arm); let unknown_variant_err = if !variants.is_empty() { let names = variants.iter().map(Variant::as_name); @@ -90,16 +89,26 @@ impl<'a> ToTokens for FromMetaImpl<'a> { } }; + let default = base + .default + .as_ref() + .map(|default_expr| quote!(::darling::export::Ok(#default_expr))); + let unit_arm_default = default.as_ref().map(|default| quote!("" => #default,)); + let list_default_or_err = default.unwrap_or(quote! { + ::darling::export::Err(::darling::Error::too_few_items(1)) + }); + quote!( fn from_list(__outer: &[::darling::export::NestedMeta]) -> ::darling::Result { // An enum must have exactly one value inside the parentheses if it's not a unit - // match arm + // match arm. + // NOTE: The empty list case uses the annotated default value for the enum if any. match __outer.len() { - 0 => ::darling::export::Err(::darling::Error::too_few_items(1)), + 0 => #list_default_or_err, 1 => { if let ::darling::export::NestedMeta::Meta(ref __nested) = __outer[0] { match ::darling::util::path_to_string(__nested.path()).as_ref() { - #(#struct_arms)* + #(#variants)* __other => ::darling::export::Err(::darling::Error::#unknown_variant_err.with_span(__nested)) } } else { @@ -113,6 +122,7 @@ impl<'a> ToTokens for FromMetaImpl<'a> { fn from_string(lit: &str) -> ::darling::Result { match lit { #(#unit_arms)* + #unit_arm_default __other => ::darling::export::Err(::darling::Error::unknown_value(__other)) } } diff --git a/core/src/codegen/variant.rs b/core/src/codegen/variant.rs index ccbabad..2123151 100644 --- a/core/src/codegen/variant.rs +++ b/core/src/codegen/variant.rs @@ -53,6 +53,16 @@ impl<'a> UsesTypeParams for Variant<'a> { } } +impl<'a> ToTokens for Variant<'a> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.data.is_unit() { + self.as_unit_match_arm().to_tokens(tokens); + } else { + self.as_data_match_arm().to_tokens(tokens) + } + } +} + /// Code generator for an enum variant in a unit match position. /// This is placed in generated `from_string` calls for the parent enum. /// Value-carrying variants wrapped in this type will emit code to produce an "unsupported format" error. diff --git a/tests/from_meta.rs b/tests/from_meta.rs index 3e7278c..73d0074 100644 --- a/tests/from_meta.rs +++ b/tests/from_meta.rs @@ -64,3 +64,55 @@ fn nested_meta_lit_bool_errors() { Error::unsupported_format("literal").to_string() ); } + +/// Tests behavior of FromMeta implementation for enums (including behavior of defaults). +mod enum_impl { + use darling::{Error, FromMeta}; + use syn::parse_quote; + + /// A playback volume. + /// Deriving `Default` will cause the "default" variant to be set when the meta-item is empty. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, FromMeta)] + #[darling(default)] + enum Volume { + #[default] + Normal, + Low, + High, + #[darling(rename = "dB")] + Decibels(u8), + } + + #[test] + fn empty_list() { + let volume = Volume::from_list(&[]).unwrap(); + assert_eq!(volume, Volume::Normal); + } + + #[test] + fn empty_string() { + let volume = Volume::from_string("").unwrap(); + assert_eq!(volume, Volume::Normal); + } + + #[test] + fn single_value_list() { + let unit_variant = Volume::from_list(&[parse_quote!(high)]).unwrap(); + assert_eq!(unit_variant, Volume::High); + + let newtype_variant = Volume::from_list(&[parse_quote!(dB = 100)]).unwrap(); + assert_eq!(newtype_variant, Volume::Decibels(100)); + } + + #[test] + fn string_for_unit_variant() { + let volume = Volume::from_string("low").unwrap(); + assert_eq!(volume, Volume::Low); + } + + #[test] + fn multiple_values_list_errors() { + let err = Volume::from_list(&[parse_quote!(low), parse_quote!(dB = 20)]).unwrap_err(); + assert_eq!(err.to_string(), Error::too_many_items(1).to_string()); + } +}