1 | use std::borrow::Cow; |
2 | |
3 | use proc_macro2::TokenStream; |
4 | use quote::ToTokens; |
5 | use syn::parse_quote; |
6 | |
7 | use crate::ast::Data; |
8 | use crate::codegen::FromMetaImpl; |
9 | use crate::error::Accumulator; |
10 | use crate::options::{Core, ParseAttribute, ParseData}; |
11 | use crate::util::Callable; |
12 | use crate::{Error, FromMeta, Result}; |
13 | |
14 | pub 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 | } |
21 | |
22 | impl FromMetaOptions { |
23 | pub fn new(di: &syn::DeriveInput) -> Result<Self> { |
24 | (FromMetaOptions { |
25 | base: Core::start(di)?, |
26 | from_word: None, |
27 | from_none: None, |
28 | }) |
29 | .parse_attributes(&di.attrs)? |
30 | .parse_body(&di.data) |
31 | } |
32 | |
33 | /// Get the `from_word` method body, if one exists. This can come from direct use of |
34 | /// `#[darling(from_word = ...)]` on the container or from use of `#[darling(word)]` on |
35 | /// a unit variant. |
36 | #[allow ( |
37 | clippy::wrong_self_convention, |
38 | // The reason is commented out due to MSRV issues. |
39 | // reason = "This matches the name of the input option and output method" |
40 | )] |
41 | fn from_word(&self) -> Option<Cow<'_, Callable>> { |
42 | self.from_word.as_ref().map(Cow::Borrowed).or_else(|| { |
43 | if let Data::Enum(ref variants) = self.base.data { |
44 | // The first variant which has `word` set to `true`. |
45 | // This assumes that validation has prevented multiple variants |
46 | // from claiming `word`. |
47 | let variant = variants |
48 | .iter() |
49 | .find(|v| v.word.map(|x| *x).unwrap_or_default())?; |
50 | let variant_ident = &variant.ident; |
51 | let closure: syn::ExprClosure = parse_quote! { |
52 | || ::darling::export::Ok(Self::#variant_ident) |
53 | }; |
54 | Some(Cow::Owned(Callable::from(closure))) |
55 | } else { |
56 | None |
57 | } |
58 | }) |
59 | } |
60 | } |
61 | |
62 | impl ParseAttribute for FromMetaOptions { |
63 | fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> { |
64 | let path: &Path = mi.path(); |
65 | |
66 | if path.is_ident("from_word" ) { |
67 | if self.from_word.is_some() { |
68 | return Err(Error::duplicate_field_path(path).with_span(node:path)); |
69 | } |
70 | |
71 | self.from_word = FromMeta::from_meta(mi).map(op:Some)?; |
72 | } else if path.is_ident("from_none" ) { |
73 | if self.from_none.is_some() { |
74 | return Err(Error::duplicate_field_path(path).with_span(node:path)); |
75 | } |
76 | |
77 | self.from_none = FromMeta::from_meta(mi).map(op:Some)?; |
78 | } else { |
79 | self.base.parse_nested(mi)?; |
80 | } |
81 | |
82 | Ok(()) |
83 | } |
84 | } |
85 | |
86 | impl ParseData for FromMetaOptions { |
87 | fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> { |
88 | self.base.parse_variant(variant) |
89 | } |
90 | |
91 | fn parse_field(&mut self, field: &syn::Field) -> Result<()> { |
92 | self.base.parse_field(field) |
93 | } |
94 | |
95 | fn validate_body(&self, errors: &mut Accumulator) { |
96 | self.base.validate_body(errors); |
97 | |
98 | match self.base.data { |
99 | Data::Struct(ref data) => { |
100 | if let Some(from_word) = &self.from_word { |
101 | if data.is_unit() { |
102 | errors.push(Error::custom("`from_word` cannot be used on unit structs because it conflicts with the generated impl" ).with_span(from_word)); |
103 | } else if data.is_newtype() { |
104 | 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)); |
105 | } |
106 | } |
107 | } |
108 | Data::Enum(ref data) => { |
109 | let word_variants: Vec<_> = data |
110 | .iter() |
111 | .filter_map(|variant| variant.word.as_ref()) |
112 | .collect(); |
113 | |
114 | if !word_variants.is_empty() { |
115 | if let Some(from_word) = &self.from_word { |
116 | errors.push( |
117 | Error::custom( |
118 | "`from_word` cannot be used with an enum that also uses `word`" , |
119 | ) |
120 | .with_span(from_word), |
121 | ) |
122 | } |
123 | } |
124 | |
125 | // Adds errors for duplicate `#[darling(word)]` annotations across all variants. |
126 | if word_variants.len() > 1 { |
127 | for word in word_variants { |
128 | errors.push( |
129 | Error::custom("`#[darling(word)]` can only be applied to one variant" ) |
130 | .with_span(&word.span()), |
131 | ); |
132 | } |
133 | } |
134 | } |
135 | } |
136 | } |
137 | } |
138 | |
139 | impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> { |
140 | fn from(v: &'a FromMetaOptions) -> Self { |
141 | FromMetaImpl { |
142 | base: (&v.base).into(), |
143 | from_word: v.from_word(), |
144 | from_none: v.from_none.as_ref(), |
145 | } |
146 | } |
147 | } |
148 | |
149 | impl ToTokens for FromMetaOptions { |
150 | fn to_tokens(&self, tokens: &mut TokenStream) { |
151 | FromMetaImpl::from(self).to_tokens(tokens) |
152 | } |
153 | } |
154 | |