darling_core/options/
from_meta.rs
1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use quote::ToTokens;
5use syn::parse_quote;
6
7use crate::ast::Data;
8use crate::codegen::FromMetaImpl;
9use crate::error::Accumulator;
10use crate::options::{Core, ParseAttribute, ParseData};
11use crate::util::Callable;
12use crate::{Error, FromMeta, Result};
13
14pub struct FromMetaOptions {
15 base: Core,
16 from_word: Option<Callable>,
18 from_none: Option<Callable>,
20}
21
22impl FromMetaOptions {
23 pub fn new(di: &syn::DeriveInput) -> Result<Self> {
24 (FromMetaOptions {
25 base: Core::start(di)?,
26 from_word: None,
27 from_none: None,
28 })
29 .parse_attributes(&di.attrs)?
30 .parse_body(&di.data)
31 }
32
33 #[allow(
37 clippy::wrong_self_convention,
38 )]
41 fn from_word(&self) -> Option<Cow<'_, Callable>> {
42 self.from_word.as_ref().map(Cow::Borrowed).or_else(|| {
43 if let Data::Enum(ref variants) = self.base.data {
44 let variant = variants
48 .iter()
49 .find(|v| v.word.map(|x| *x).unwrap_or_default())?;
50 let variant_ident = &variant.ident;
51 let closure: syn::ExprClosure = parse_quote! {
52 || ::darling::export::Ok(Self::#variant_ident)
53 };
54 Some(Cow::Owned(Callable::from(closure)))
55 } else {
56 None
57 }
58 })
59 }
60}
61
62impl ParseAttribute for FromMetaOptions {
63 fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
64 let path = mi.path();
65
66 if path.is_ident("from_word") {
67 if self.from_word.is_some() {
68 return Err(Error::duplicate_field_path(path).with_span(path));
69 }
70
71 self.from_word = FromMeta::from_meta(mi).map(Some)?;
72 } else if path.is_ident("from_none") {
73 if self.from_none.is_some() {
74 return Err(Error::duplicate_field_path(path).with_span(path));
75 }
76
77 self.from_none = FromMeta::from_meta(mi).map(Some)?;
78 } else {
79 self.base.parse_nested(mi)?;
80 }
81
82 Ok(())
83 }
84}
85
86impl ParseData for FromMetaOptions {
87 fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
88 self.base.parse_variant(variant)
89 }
90
91 fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
92 self.base.parse_field(field)
93 }
94
95 fn validate_body(&self, errors: &mut Accumulator) {
96 self.base.validate_body(errors);
97
98 match self.base.data {
99 Data::Struct(ref data) => {
100 if let Some(from_word) = &self.from_word {
101 if data.is_unit() {
102 errors.push(Error::custom("`from_word` cannot be used on unit structs because it conflicts with the generated impl").with_span(from_word));
103 } else if data.is_newtype() {
104 errors.push(Error::custom("`from_word` cannot be used on newtype structs because the implementation is entirely delegated to the inner type").with_span(from_word));
105 }
106 }
107 }
108 Data::Enum(ref data) => {
109 let word_variants: Vec<_> = data
110 .iter()
111 .filter_map(|variant| variant.word.as_ref())
112 .collect();
113
114 if !word_variants.is_empty() {
115 if let Some(from_word) = &self.from_word {
116 errors.push(
117 Error::custom(
118 "`from_word` cannot be used with an enum that also uses `word`",
119 )
120 .with_span(from_word),
121 )
122 }
123 }
124
125 if word_variants.len() > 1 {
127 for word in word_variants {
128 errors.push(
129 Error::custom("`#[darling(word)]` can only be applied to one variant")
130 .with_span(&word.span()),
131 );
132 }
133 }
134 }
135 }
136 }
137}
138
139impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> {
140 fn from(v: &'a FromMetaOptions) -> Self {
141 FromMetaImpl {
142 base: (&v.base).into(),
143 from_word: v.from_word(),
144 from_none: v.from_none.as_ref(),
145 }
146 }
147}
148
149impl ToTokens for FromMetaOptions {
150 fn to_tokens(&self, tokens: &mut TokenStream) {
151 FromMetaImpl::from(self).to_tokens(tokens)
152 }
153}