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    pub 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 is_unit_variant(&self) -> bool {
24        self.data.is_unit()
25    }
26
27    pub fn is_skipped(&self) -> bool {
28        self.skip.unwrap_or_default()
29    }
30
31    pub fn as_codegen_variant<'a>(&'a self, ty_ident: &'a syn::Ident) -> codegen::Variant<'a> {
32        codegen::Variant {
33            ty_ident,
34            variant_ident: &self.ident,
35            name_in_attr: self
36                .attr_name
37                .as_deref()
38                .map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
39            data: self.data.as_ref().map(InputField::as_codegen_field),
40            skip: self.is_skipped(),
41            allow_unknown_fields: self.allow_unknown_fields.unwrap_or_default(),
42        }
43    }
44
45    pub fn from_variant(v: &syn::Variant, parent: Option<&Core>) -> Result<Self> {
46        let mut starter = (InputVariant {
47            ident: v.ident.clone(),
48            attr_name: Default::default(),
49            data: Fields::empty_from(&v.fields),
50            skip: Default::default(),
51            word: Default::default(),
52            allow_unknown_fields: None,
53        })
54        .parse_attributes(&v.attrs)?;
55
56        starter.data.fields = match v.fields {
57            syn::Fields::Unit => vec![],
58            syn::Fields::Unnamed(ref fields) => {
59                let mut items = Vec::with_capacity(fields.unnamed.len());
60                for item in &fields.unnamed {
61                    items.push(InputField::from_field(item, parent)?);
62                }
63
64                items
65            }
66            syn::Fields::Named(ref fields) => {
67                let mut items = Vec::with_capacity(fields.named.len());
68                for item in &fields.named {
69                    items.push(InputField::from_field(item, parent)?);
70                }
71
72                items
73            }
74        };
75
76        Ok(if let Some(p) = parent {
77            starter.with_inherited(p)
78        } else {
79            starter
80        })
81    }
82
83    fn with_inherited(mut self, parent: &Core) -> Self {
84        if self.attr_name.is_none() {
85            self.attr_name = Some(parent.rename_rule.apply_to_variant(self.ident.to_string()));
86        }
87
88        if self.allow_unknown_fields.is_none() {
89            self.allow_unknown_fields = Some(parent.allow_unknown_fields.unwrap_or_default());
90        }
91
92        self
93    }
94}
95
96impl ParseAttribute for InputVariant {
97    fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
98        let path = mi.path();
99        if path.is_ident("rename") {
100            if self.attr_name.is_some() {
101                return Err(Error::duplicate_field_path(path).with_span(mi));
102            }
103
104            self.attr_name = FromMeta::from_meta(mi)?;
105        } else if path.is_ident("skip") {
106            if self.skip.is_some() {
107                return Err(Error::duplicate_field_path(path).with_span(mi));
108            }
109
110            self.skip = FromMeta::from_meta(mi)?;
111        } else if path.is_ident("word") {
112            if self.word.is_some() {
113                return Err(Error::duplicate_field_path(path).with_span(mi));
114            }
115
116            if !self.data.is_unit() {
117                let note = "`#[darling(word)]` can only be applied to a unit variant";
118                #[cfg(feature = "diagnostics")]
119                let error = Error::unknown_field_path(path).note(note);
120                #[cfg(not(feature = "diagnostics"))]
121                let error = Error::custom(format!("Unexpected field: `word`. {}", note));
122
123                return Err(error.with_span(mi));
124            }
125
126            self.word = FromMeta::from_meta(mi)?;
127        } else {
128            return Err(Error::unknown_field_path(path).with_span(mi));
129        }
130
131        Ok(())
132    }
133}