darling_core/options/
input_variant.rs

1use std::borrow::Cow;
2
3use crate::ast::Fields;
4use crate::codegen;
5use crate::options::{Core, InputField, ParseAttribute};
6use crate::util::SpannedValue;
7use crate::{Error, FromMeta, Result};
8
9#[derive(Debug, Clone)]
10pub struct InputVariant {
11    ident: syn::Ident,
12    attr_name: Option<String>,
13    data: Fields<InputField>,
14    skip: Option<bool>,
15    /// Whether or not the variant should be used to create an instance for
16    /// `FromMeta::from_word`.
17    pub word: Option<SpannedValue<bool>>,
18    /// Whether or not unknown fields are acceptable in this
19    allow_unknown_fields: Option<bool>,
20}
21
22impl InputVariant {
23    pub fn as_codegen_variant<'a>(&'a self, ty_ident: &'a syn::Ident) -> codegen::Variant<'a> {
24        codegen::Variant {
25            ty_ident,
26            variant_ident: &self.ident,
27            name_in_attr: self
28                .attr_name
29                .as_ref()
30                .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
31            data: self.data.as_ref().map(InputField::as_codegen_field),
32            skip: self.skip.unwrap_or_default(),
33            word: *self.word.unwrap_or_default(),
34            allow_unknown_fields: self.allow_unknown_fields.unwrap_or_default(),
35        }
36    }
37
38    pub fn from_variant(v: &syn::Variant, parent: Option<&Core>) -> Result<Self> {
39        let mut starter = (InputVariant {
40            ident: v.ident.clone(),
41            attr_name: Default::default(),
42            data: Fields::empty_from(&v.fields),
43            skip: Default::default(),
44            word: Default::default(),
45            allow_unknown_fields: None,
46        })
47        .parse_attributes(&v.attrs)?;
48
49        starter.data.fields = match v.fields {
50            syn::Fields::Unit => vec![],
51            syn::Fields::Unnamed(ref fields) => {
52                let mut items = Vec::with_capacity(fields.unnamed.len());
53                for item in &fields.unnamed {
54                    items.push(InputField::from_field(item, parent)?);
55                }
56
57                items
58            }
59            syn::Fields::Named(ref fields) => {
60                let mut items = Vec::with_capacity(fields.named.len());
61                for item in &fields.named {
62                    items.push(InputField::from_field(item, parent)?);
63                }
64
65                items
66            }
67        };
68
69        Ok(if let Some(p) = parent {
70            starter.with_inherited(p)
71        } else {
72            starter
73        })
74    }
75
76    fn with_inherited(mut self, parent: &Core) -> Self {
77        if self.attr_name.is_none() {
78            self.attr_name = Some(parent.rename_rule.apply_to_variant(self.ident.to_string()));
79        }
80
81        if self.allow_unknown_fields.is_none() {
82            self.allow_unknown_fields = Some(parent.allow_unknown_fields.unwrap_or_default());
83        }
84
85        self
86    }
87}
88
89impl ParseAttribute for InputVariant {
90    fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
91        let path = mi.path();
92        if path.is_ident("rename") {
93            if self.attr_name.is_some() {
94                return Err(Error::duplicate_field_path(path).with_span(mi));
95            }
96
97            self.attr_name = FromMeta::from_meta(mi)?;
98        } else if path.is_ident("skip") {
99            if self.skip.is_some() {
100                return Err(Error::duplicate_field_path(path).with_span(mi));
101            }
102
103            self.skip = FromMeta::from_meta(mi)?;
104        } else if path.is_ident("word") {
105            if self.word.is_some() {
106                return Err(Error::duplicate_field_path(path).with_span(mi));
107            }
108
109            if !self.data.is_unit() {
110                let note = "`#[darling(word)]` can only be applied to a unit variant";
111                #[cfg(feature = "diagnostics")]
112                let error = Error::unknown_field_path(path).note(note);
113                #[cfg(not(feature = "diagnostics"))]
114                let error = Error::custom(format!("Unexpected field: `word`. {}", note));
115
116                return Err(error.with_span(mi));
117            }
118
119            self.word = FromMeta::from_meta(mi)?;
120        } else {
121            return Err(Error::unknown_field_path(path).with_span(mi));
122        }
123
124        Ok(())
125    }
126}