1 | use std::borrow::Cow; |
2 | |
3 | use proc_macro2::TokenStream; |
4 | use quote::{quote, ToTokens, TokenStreamExt}; |
5 | use syn::Ident; |
6 | |
7 | use crate::ast::Fields; |
8 | use crate::codegen::error::{ErrorCheck, ErrorDeclaration}; |
9 | use crate::codegen::{Field, FieldsGen}; |
10 | use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams}; |
11 | |
12 | /// A variant of the enum which is deriving `FromMeta`. |
13 | #[derive (Debug, Clone)] |
14 | pub struct Variant<'a> { |
15 | /// The name which will appear in code passed to the `FromMeta` input. |
16 | pub name_in_attr: Cow<'a, String>, |
17 | |
18 | /// The name of the variant which will be returned for a given `name_in_attr`. |
19 | pub variant_ident: &'a Ident, |
20 | |
21 | /// The name of the parent enum type. |
22 | pub ty_ident: &'a Ident, |
23 | |
24 | pub data: Fields<Field<'a>>, |
25 | |
26 | /// Whether or not the variant should be skipped in the generated code. |
27 | pub skip: bool, |
28 | |
29 | pub allow_unknown_fields: bool, |
30 | } |
31 | |
32 | impl<'a> Variant<'a> { |
33 | pub fn as_name(&'a self) -> &'a str { |
34 | &self.name_in_attr |
35 | } |
36 | |
37 | pub fn as_unit_match_arm(&'a self) -> UnitMatchArm<'a> { |
38 | UnitMatchArm(self) |
39 | } |
40 | |
41 | pub fn as_data_match_arm(&'a self) -> DataMatchArm<'a> { |
42 | DataMatchArm(self) |
43 | } |
44 | } |
45 | |
46 | impl UsesTypeParams for Variant<'_> { |
47 | fn uses_type_params<'b>( |
48 | &self, |
49 | options: &usage::Options, |
50 | type_set: &'b IdentSet, |
51 | ) -> IdentRefSet<'b> { |
52 | self.data.uses_type_params(options, type_set) |
53 | } |
54 | } |
55 | |
56 | /// Code generator for an enum variant in a unit match position. |
57 | /// This is placed in generated `from_string` calls for the parent enum. |
58 | /// Value-carrying variants wrapped in this type will emit code to produce an "unsupported format" error. |
59 | pub struct UnitMatchArm<'a>(&'a Variant<'a>); |
60 | |
61 | impl ToTokens for UnitMatchArm<'_> { |
62 | fn to_tokens(&self, tokens: &mut TokenStream) { |
63 | let val: &Variant<'_> = self.0; |
64 | |
65 | if val.skip { |
66 | return; |
67 | } |
68 | |
69 | let name_in_attr = &val.name_in_attr; |
70 | |
71 | let unsupported_format_error = || { |
72 | quote!(::darling::export::Err( |
73 | ::darling::Error::unsupported_format("literal" ) |
74 | )) |
75 | }; |
76 | |
77 | if val.data.is_unit() { |
78 | let variant_ident = val.variant_ident; |
79 | let ty_ident = val.ty_ident; |
80 | |
81 | tokens.append_all(quote!( |
82 | #name_in_attr => ::darling::export::Ok(#ty_ident::#variant_ident), |
83 | )); |
84 | } else if val.data.is_newtype() { |
85 | let field = val |
86 | .data |
87 | .fields |
88 | .first() |
89 | .expect("Newtype should have exactly one field" ); |
90 | let field_ty = field.ty; |
91 | let ty_ident = val.ty_ident; |
92 | let variant_ident = val.variant_ident; |
93 | let unsupported_format = unsupported_format_error(); |
94 | |
95 | tokens.append_all(quote!{ |
96 | #name_in_attr => { |
97 | match <#field_ty as ::darling::FromMeta>::from_none() { |
98 | ::darling::export::Some(__value) => ::darling::export::Ok(#ty_ident::#variant_ident(__value)), |
99 | ::darling::export::None => #unsupported_format, |
100 | } |
101 | } |
102 | }) |
103 | } else { |
104 | let unsupported_format = unsupported_format_error(); |
105 | tokens.append_all(quote!( |
106 | #name_in_attr => #unsupported_format, |
107 | )); |
108 | } |
109 | } |
110 | } |
111 | |
112 | /// Code generator for an enum variant in a data-carrying match position. |
113 | /// This is placed in generated `from_list` calls for the parent enum. |
114 | /// Unit variants wrapped in this type will emit code to produce an "unsupported format" error. |
115 | pub struct DataMatchArm<'a>(&'a Variant<'a>); |
116 | |
117 | impl ToTokens for DataMatchArm<'_> { |
118 | fn to_tokens(&self, tokens: &mut TokenStream) { |
119 | let val: &Variant<'_> = self.0; |
120 | |
121 | if val.skip { |
122 | return; |
123 | } |
124 | |
125 | let name_in_attr = &val.name_in_attr; |
126 | let variant_ident = val.variant_ident; |
127 | let ty_ident = val.ty_ident; |
128 | |
129 | if val.data.is_unit() { |
130 | // Allow unit variants to match a list item if it's just a path with no associated |
131 | // value, e.g. `volume(shout)` is allowed. |
132 | tokens.append_all(quote!( |
133 | #name_in_attr => { |
134 | if let ::darling::export::syn::Meta::Path(_) = *__nested { |
135 | ::darling::export::Ok(#ty_ident::#variant_ident) |
136 | } else { |
137 | ::darling::export::Err(::darling::Error::unsupported_format("non-path" )) |
138 | } |
139 | }, |
140 | )); |
141 | |
142 | return; |
143 | } |
144 | |
145 | let vdg = FieldsGen::new(&val.data, val.allow_unknown_fields); |
146 | |
147 | if val.data.is_struct() { |
148 | let declare_errors = ErrorDeclaration::default(); |
149 | let check_errors = ErrorCheck::with_location(name_in_attr); |
150 | let require_fields = vdg.require_fields(); |
151 | let decls = vdg.declarations(); |
152 | let core_loop = vdg.core_loop(); |
153 | let inits = vdg.initializers(); |
154 | |
155 | tokens.append_all(quote!( |
156 | #name_in_attr => { |
157 | if let ::darling::export::syn::Meta::List(ref __data) = *__nested { |
158 | let __items = ::darling::export::NestedMeta::parse_meta_list(__data.tokens.clone())?; |
159 | let __items = &__items; |
160 | |
161 | #declare_errors |
162 | |
163 | #decls |
164 | |
165 | #core_loop |
166 | |
167 | #require_fields |
168 | |
169 | #check_errors |
170 | |
171 | ::darling::export::Ok(#ty_ident::#variant_ident { |
172 | #inits |
173 | }) |
174 | } else { |
175 | ::darling::export::Err(::darling::Error::unsupported_format("non-list" )) |
176 | } |
177 | } |
178 | )); |
179 | } else if val.data.is_newtype() { |
180 | tokens.append_all(quote!( |
181 | #name_in_attr => { |
182 | ::darling::export::Ok( |
183 | #ty_ident::#variant_ident( |
184 | ::darling::FromMeta::from_meta(__nested) |
185 | .map_err(|e| e.at(#name_in_attr))?) |
186 | ) |
187 | } |
188 | )); |
189 | } else { |
190 | panic!("Match arms aren't supported for tuple variants yet" ); |
191 | } |
192 | } |
193 | } |
194 | |