darling_core/options/
input_field.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
use std::borrow::Cow;

use syn::{parse_quote_spanned, spanned::Spanned};

use crate::codegen;
use crate::options::{Core, DefaultExpression, ParseAttribute};
use crate::util::{Flag, SpannedValue};
use crate::{Error, FromMeta, Result};

#[derive(Debug, Clone)]
pub struct InputField {
    pub ident: syn::Ident,
    pub attr_name: Option<String>,
    pub ty: syn::Type,
    pub default: Option<DefaultExpression>,
    pub with: Option<syn::Path>,

    /// If `true`, generated code will not look for this field in the input meta item,
    /// instead always falling back to either `InputField::default` or `Default::default`.
    pub skip: Option<SpannedValue<bool>>,
    pub post_transform: Option<codegen::PostfixTransform>,
    pub multiple: Option<bool>,
    pub flatten: Flag,
}

impl InputField {
    /// Generate a view into this field that can be used for code generation.
    pub fn as_codegen_field(&self) -> codegen::Field<'_> {
        codegen::Field {
            ident: &self.ident,
            name_in_attr: self
                .attr_name
                .as_ref()
                .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
            ty: &self.ty,
            default_expression: self.as_codegen_default(),
            with_path: self.with.as_ref().map_or_else(
                || {
                    Cow::Owned(
                        parse_quote_spanned!(self.ty.span()=> ::darling::FromMeta::from_meta),
                    )
                },
                Cow::Borrowed,
            ),
            skip: *self.skip.unwrap_or_default(),
            post_transform: self.post_transform.as_ref(),
            multiple: self.multiple.unwrap_or_default(),
            flatten: self.flatten.is_present(),
        }
    }

    /// Generate a codegen::DefaultExpression for this field. This requires the field name
    /// in the `Inherit` case.
    fn as_codegen_default(&self) -> Option<codegen::DefaultExpression<'_>> {
        self.default.as_ref().map(|expr| match *expr {
            DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
            DefaultExpression::Inherit => codegen::DefaultExpression::Inherit(&self.ident),
            DefaultExpression::Trait { span } => codegen::DefaultExpression::Trait { span },
        })
    }

    fn new(ident: syn::Ident, ty: syn::Type) -> Self {
        InputField {
            ident,
            ty,
            attr_name: None,
            default: None,
            with: None,
            skip: None,
            post_transform: Default::default(),
            multiple: None,
            flatten: Default::default(),
        }
    }

    pub fn from_field(f: &syn::Field, parent: Option<&Core>) -> Result<Self> {
        let ident = f
            .ident
            .clone()
            .unwrap_or_else(|| syn::Ident::new("__unnamed", ::proc_macro2::Span::call_site()));
        let ty = f.ty.clone();
        let base = Self::new(ident, ty).parse_attributes(&f.attrs)?;

        Ok(if let Some(container) = parent {
            base.with_inherited(container)
        } else {
            base
        })
    }

    /// Apply inherited settings from the container. This is done _after_ parsing
    /// to ensure deference to explicit field-level settings.
    fn with_inherited(mut self, parent: &Core) -> Self {
        // explicit renamings take precedence over rename rules on the container,
        // but in the absence of an explicit name we apply the rule.
        if self.attr_name.is_none() {
            self.attr_name = Some(parent.rename_rule.apply_to_field(self.ident.to_string()));
        }

        // Determine the default expression for this field, based on three pieces of information:
        // 1. Will we look for this field in the attribute?
        // 1. Is there a locally-defined default?
        // 1. Did the parent define a default?
        self.default = match (&self.skip, self.default.is_some(), parent.default.is_some()) {
            // If we have a default, use it.
            (_, true, _) => self.default,

            // If there isn't an explicit default but the struct sets a default, we'll
            // inherit from that.
            (_, false, true) => Some(DefaultExpression::Inherit),

            // If we're skipping the field and no defaults have been expressed then we should
            // use the ::darling::export::Default trait, and set the span to the skip keyword
            // so that an error caused by the skipped field's type not implementing `Default`
            // will correctly identify why darling is trying to use `Default`.
            (Some(v), false, false) if **v => Some(DefaultExpression::Trait { span: v.span() }),

            // If we don't have or need a default, then leave it blank.
            (_, false, false) => None,
        };

        self
    }
}

impl ParseAttribute for InputField {
    fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
        let path = mi.path();

        if path.is_ident("rename") {
            if self.attr_name.is_some() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }

            self.attr_name = FromMeta::from_meta(mi)?;

            if self.flatten.is_present() {
                return Err(
                    Error::custom("`flatten` and `rename` cannot be used together").with_span(mi),
                );
            }
        } else if path.is_ident("default") {
            if self.default.is_some() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }
            self.default = FromMeta::from_meta(mi)?;
        } else if path.is_ident("with") {
            if self.with.is_some() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }

            self.with = Some(FromMeta::from_meta(mi)?);

            if self.flatten.is_present() {
                return Err(
                    Error::custom("`flatten` and `with` cannot be used together").with_span(mi),
                );
            }
        } else if path.is_ident("skip") {
            if self.skip.is_some() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }

            self.skip = FromMeta::from_meta(mi)?;

            if self.skip.map(|v| *v).unwrap_or_default() && self.flatten.is_present() {
                return Err(
                    Error::custom("`flatten` and `skip` cannot be used together").with_span(mi),
                );
            }
        } else if path.is_ident("map") || path.is_ident("and_then") {
            let transformer = path.get_ident().unwrap().clone();
            if let Some(post_transform) = &self.post_transform {
                if transformer == post_transform.transformer {
                    return Err(Error::duplicate_field_path(path).with_span(mi));
                } else {
                    return Err(Error::custom(format!(
                        "Options `{}` and `{}` are mutually exclusive",
                        transformer, post_transform.transformer
                    ))
                    .with_span(mi));
                }
            }

            self.post_transform = Some(codegen::PostfixTransform::new(
                transformer,
                FromMeta::from_meta(mi)?,
            ));
        } else if path.is_ident("multiple") {
            if self.multiple.is_some() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }

            self.multiple = FromMeta::from_meta(mi)?;

            if self.multiple == Some(true) && self.flatten.is_present() {
                return Err(
                    Error::custom("`flatten` and `multiple` cannot be used together").with_span(mi),
                );
            }
        } else if path.is_ident("flatten") {
            if self.flatten.is_present() {
                return Err(Error::duplicate_field_path(path).with_span(mi));
            }

            self.flatten = FromMeta::from_meta(mi)?;

            let mut conflicts = Error::accumulator();

            if self.multiple == Some(true) {
                conflicts.push(
                    Error::custom("`flatten` and `multiple` cannot be used together").with_span(mi),
                );
            }

            if self.attr_name.is_some() {
                conflicts.push(
                    Error::custom("`flatten` and `rename` cannot be used together").with_span(mi),
                );
            }

            if self.with.is_some() {
                conflicts.push(
                    Error::custom("`flatten` and `with` cannot be used together").with_span(mi),
                );
            }

            if self.skip.map(|v| *v).unwrap_or_default() {
                conflicts.push(
                    Error::custom("`flatten` and `skip` cannot be used together").with_span(mi),
                );
            }

            conflicts.finish()?;
        } else {
            return Err(Error::unknown_field_path(path).with_span(mi));
        }

        Ok(())
    }
}