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    /// Override for the default [`FromMeta::from_word`] method.
17    from_word: Option<Callable>,
18    /// Override for the default [`FromMeta::from_none`] method.
19    from_none: Option<Callable>,
20    /// Override for the default [`FromMeta::from_expr`] method.
21    from_expr: Option<Callable>,
22    /// Whether or not to derive [`syn::parse::Parse`] in addition to deriving [`FromMeta`].
23    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    /// Get the `from_word` method body, if one exists. This can come from direct use of
40    /// `#[darling(from_word = ...)]` on the container or from use of `#[darling(word)]` on
41    /// a unit variant.
42    #[allow(
43        clippy::wrong_self_convention,
44        // The reason is commented out due to MSRV issues.
45        // reason = "This matches the name of the input option and output method"
46    )]
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                // The first variant which has `word` set to `true`.
51                // This assumes that validation has prevented multiple variants
52                // from claiming `word`.
53                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                // Adds errors for duplicate `#[darling(word)]` annotations across all variants.
150                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}