diff --git a/core/src/codegen/from_meta_impl.rs b/core/src/codegen/from_meta_impl.rs index 2d0013f..4c59627 100644 --- a/core/src/codegen/from_meta_impl.rs +++ b/core/src/codegen/from_meta_impl.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; use syn::spanned::Spanned; @@ -8,7 +10,7 @@ use crate::util::Callable; pub struct FromMetaImpl<'a> { pub base: TraitImpl<'a>, - pub from_word: Option<&'a Callable>, + pub from_word: Option>, pub from_none: Option<&'a Callable>, } @@ -16,7 +18,7 @@ impl ToTokens for FromMetaImpl<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let base = &self.base; - let from_word = self.from_word.map(|body| { + let from_word = self.from_word.as_ref().map(|body| { quote_spanned! {body.span()=> fn from_word() -> ::darling::Result { ::darling::export::identity:: ::darling::Result>(#body)() @@ -113,23 +115,6 @@ impl ToTokens for FromMetaImpl<'_> { } }; - let from_word_method = variants - .iter() - .find_map(|variant| { - if variant.word { - let ty_ident = variant.ty_ident; - let variant_ident = variant.variant_ident; - Some(quote! { - fn from_word() -> ::darling::Result { - ::darling::export::Ok(#ty_ident::#variant_ident) - } - }) - } else { - None - } - }) - .or(from_word); - let data_variants = variants.iter().map(Variant::as_data_match_arm); quote!( @@ -159,7 +144,7 @@ impl ToTokens for FromMetaImpl<'_> { } } - #from_word_method + #from_word #from_none ) diff --git a/core/src/codegen/variant.rs b/core/src/codegen/variant.rs index 8fd742a..9c1f6e5 100644 --- a/core/src/codegen/variant.rs +++ b/core/src/codegen/variant.rs @@ -26,10 +26,6 @@ pub struct Variant<'a> { /// Whether or not the variant should be skipped in the generated code. pub skip: bool, - /// Whether or not the variant should be used to create an instance for - /// `FromMeta::from_word`. - pub word: bool, - pub allow_unknown_fields: bool, } diff --git a/core/src/options/from_meta.rs b/core/src/options/from_meta.rs index ff11b89..cfb7bd8 100644 --- a/core/src/options/from_meta.rs +++ b/core/src/options/from_meta.rs @@ -1,5 +1,8 @@ +use std::borrow::Cow; + use proc_macro2::TokenStream; use quote::ToTokens; +use syn::parse_quote; use crate::ast::Data; use crate::codegen::FromMetaImpl; @@ -26,6 +29,34 @@ impl FromMetaOptions { .parse_attributes(&di.attrs)? .parse_body(&di.data) } + + /// Get the `from_word` method body, if one exists. This can come from direct use of + /// `#[darling(from_word = ...)]` on the container or from use of `#[darling(word)]` on + /// a unit variant. + #[allow( + clippy::wrong_self_convention, + // The reason is commented out due to MSRV issues. + // reason = "This matches the name of the input option and output method" + )] + fn from_word(&self) -> Option> { + self.from_word.as_ref().map(Cow::Borrowed).or_else(|| { + if let Data::Enum(ref variants) = self.base.data { + // The first variant which has `word` set to `true`. + // This assumes that validation has prevented multiple variants + // from claiming `word`. + let variant = variants + .iter() + .find(|v| v.word.map(|x| *x).unwrap_or_default())?; + let variant_ident = &variant.ident; + let closure: syn::ExprClosure = parse_quote! { + || ::darling::export::Ok(Self::#variant_ident) + }; + Some(Cow::Owned(Callable::from(closure))) + } else { + None + } + }) + } } impl ParseAttribute for FromMetaOptions { @@ -109,7 +140,7 @@ impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> { fn from(v: &'a FromMetaOptions) -> Self { FromMetaImpl { base: (&v.base).into(), - from_word: v.from_word.as_ref(), + from_word: v.from_word(), from_none: v.from_none.as_ref(), } } diff --git a/core/src/options/input_variant.rs b/core/src/options/input_variant.rs index 05b9e27..443de7e 100644 --- a/core/src/options/input_variant.rs +++ b/core/src/options/input_variant.rs @@ -8,7 +8,7 @@ use crate::{Error, FromMeta, Result}; #[derive(Debug, Clone)] pub struct InputVariant { - ident: syn::Ident, + pub ident: syn::Ident, attr_name: Option, data: Fields, skip: Option, @@ -30,7 +30,6 @@ impl InputVariant { .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed), data: self.data.as_ref().map(InputField::as_codegen_field), skip: self.skip.unwrap_or_default(), - word: *self.word.unwrap_or_default(), allow_unknown_fields: self.allow_unknown_fields.unwrap_or_default(), } } diff --git a/core/src/util/callable.rs b/core/src/util/callable.rs index dc79fb1..c726679 100644 --- a/core/src/util/callable.rs +++ b/core/src/util/callable.rs @@ -24,6 +24,22 @@ impl AsRef for Callable { } } +impl From for Callable { + fn from(value: syn::ExprPath) -> Self { + Self { + call: syn::Expr::Path(value), + } + } +} + +impl From for Callable { + fn from(value: syn::ExprClosure) -> Self { + Self { + call: syn::Expr::Closure(value), + } + } +} + impl From for syn::Expr { fn from(value: Callable) -> Self { value.call