Skip to content

Commit

Permalink
Move generation of from_word body for unit variants
Browse files Browse the repository at this point in the history
  • Loading branch information
TedDriggs committed Jan 22, 2025
1 parent d135cdc commit c22b366
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 27 deletions.
25 changes: 5 additions & 20 deletions core/src/codegen/from_meta_impl.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
Expand All @@ -8,15 +10,15 @@ use crate::util::Callable;

pub struct FromMetaImpl<'a> {
pub base: TraitImpl<'a>,
pub from_word: Option<&'a Callable>,
pub from_word: Option<Cow<'a, Callable>>,
pub from_none: Option<&'a Callable>,
}

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<Self> {
::darling::export::identity::<fn() -> ::darling::Result<Self>>(#body)()
Expand Down Expand Up @@ -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<Self> {
::darling::export::Ok(#ty_ident::#variant_ident)
}
})
} else {
None
}
})
.or(from_word);

let data_variants = variants.iter().map(Variant::as_data_match_arm);

quote!(
Expand Down Expand Up @@ -159,7 +144,7 @@ impl ToTokens for FromMetaImpl<'_> {
}
}

#from_word_method
#from_word

#from_none
)
Expand Down
4 changes: 0 additions & 4 deletions core/src/codegen/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down
28 changes: 27 additions & 1 deletion core/src/options/from_meta.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -26,6 +29,29 @@ 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.
fn from_word(&self) -> Option<Cow<'_, Callable>> {
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 {
Expand Down Expand Up @@ -109,7 +135,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(),
}
}
Expand Down
3 changes: 1 addition & 2 deletions core/src/options/input_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
data: Fields<InputField>,
skip: Option<bool>,
Expand All @@ -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(),
}
}
Expand Down
16 changes: 16 additions & 0 deletions core/src/util/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ impl AsRef<syn::Expr> for Callable {
}
}

impl From<syn::ExprPath> for Callable {
fn from(value: syn::ExprPath) -> Self {
Self {
call: syn::Expr::Path(value),
}
}
}

impl From<syn::ExprClosure> for Callable {
fn from(value: syn::ExprClosure) -> Self {
Self {
call: syn::Expr::Closure(value),
}
}
}

impl From<Callable> for syn::Expr {
fn from(value: Callable) -> Self {
value.call
Expand Down

0 comments on commit c22b366

Please sign in to comment.