darling_core/options/
from_meta.rs1use 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 from_expr: Option<Callable>,
22 derive_syn_parse: Option<bool>,
24}
25
26impl FromMetaOptions {
27 pub fn new(di: &syn::DeriveInput) -> Result<Self> {
28 (FromMetaOptions {
29 base: Core::start(di)?,
30 from_word: None,
31 from_none: None,
32 from_expr: None,
33 derive_syn_parse: None,
34 })
35 .parse_attributes(&di.attrs)?
36 .parse_body(&di.data)
37 }
38
39 #[allow(
43 clippy::wrong_self_convention,
44 )]
47 fn from_word(&self) -> Option<Cow<'_, Callable>> {
48 self.from_word.as_ref().map(Cow::Borrowed).or_else(|| {
49 if let Data::Enum(ref variants) = self.base.data {
50 let variant = variants
54 .iter()
55 .find(|v| v.word.map(|x| *x).unwrap_or_default())?;
56 let variant_ident = &variant.ident;
57 let closure: syn::ExprClosure = parse_quote! {
58 || ::darling::export::Ok(Self::#variant_ident)
59 };
60 Some(Cow::Owned(Callable::from(closure)))
61 } else {
62 None
63 }
64 })
65 }
66}
67
68impl ParseAttribute for FromMetaOptions {
69 fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
70 let path = mi.path();
71
72 if path.is_ident("from_word") {
73 if self.from_word.is_some() {
74 return Err(Error::duplicate_field_path(path).with_span(path));
75 }
76
77 self.from_word = FromMeta::from_meta(mi).map(Some)?;
78 } else if path.is_ident("from_none") {
79 if self.from_none.is_some() {
80 return Err(Error::duplicate_field_path(path).with_span(path));
81 }
82
83 self.from_none = FromMeta::from_meta(mi).map(Some)?;
84 } else if path.is_ident("from_expr") {
85 if self.from_expr.is_some() {
86 return Err(Error::duplicate_field_path(path).with_span(path));
87 }
88
89 self.from_expr = FromMeta::from_meta(mi).map(Some)?;
90 } else if path.is_ident("derive_syn_parse") {
91 if self.derive_syn_parse.is_some() {
92 return Err(Error::duplicate_field_path(path).with_span(path));
93 }
94
95 self.derive_syn_parse = FromMeta::from_meta(mi).map(Some)?;
96 } else {
97 self.base.parse_nested(mi)?;
98 }
99
100 Ok(())
101 }
102}
103
104impl ParseData for FromMetaOptions {
105 fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
106 self.base.parse_variant(variant)
107 }
108
109 fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
110 self.base.parse_field(field)
111 }
112
113 fn validate_body(&self, errors: &mut Accumulator) {
114 self.base.validate_body(errors);
115
116 match self.base.data {
117 Data::Struct(ref data) => {
118 if let Some(from_word) = &self.from_word {
119 if data.is_unit() {
120 errors.push(Error::custom("`from_word` cannot be used on unit structs because it conflicts with the generated impl").with_span(from_word));
121 } else if data.is_newtype() {
122 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));
123 }
124 }
125
126 if let Some(from_expr) = &self.from_expr {
127 if data.is_newtype() {
128 errors.push(Error::custom("`from_expr` cannot be used on newtype structs because the implementation is entirely delegated to the inner type").with_span(from_expr));
129 }
130 }
131 }
132 Data::Enum(ref data) => {
133 let word_variants: Vec<_> = data
134 .iter()
135 .filter_map(|variant| variant.word.as_ref())
136 .collect();
137
138 if !word_variants.is_empty() {
139 if let Some(from_word) = &self.from_word {
140 errors.push(
141 Error::custom(
142 "`from_word` cannot be used with an enum that also uses `word`",
143 )
144 .with_span(from_word),
145 )
146 }
147 }
148
149 if word_variants.len() > 1 {
151 for word in word_variants {
152 errors.push(
153 Error::custom("`#[darling(word)]` can only be applied to one variant")
154 .with_span(&word.span()),
155 );
156 }
157 }
158
159 if let Some(from_expr) = &self.from_expr {
160 if data.iter().any(|v| v.is_unit_variant() && !v.is_skipped()) {
161 errors.push(
162 Error::custom("`from_expr` cannot be used on enums with non-skipped unit variants because it conflicts with the generated impl")
163 .with_span(from_expr),
164 );
165 }
166 }
167 }
168 }
169 }
170}
171
172impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> {
173 fn from(v: &'a FromMetaOptions) -> Self {
174 FromMetaImpl {
175 base: (&v.base).into(),
176 from_word: v.from_word(),
177 from_none: v.from_none.as_ref(),
178 from_expr: v.from_expr.as_ref(),
179 derive_syn_parse: v.derive_syn_parse.unwrap_or_default(),
180 }
181 }
182}
183
184impl ToTokens for FromMetaOptions {
185 fn to_tokens(&self, tokens: &mut TokenStream) {
186 FromMetaImpl::from(self).to_tokens(tokens)
187 }
188}