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 | /// Whether or not the variant should be used to create an instance for |
30 | /// `FromMeta::from_word`. |
31 | pub word: bool, |
32 | |
33 | pub allow_unknown_fields: bool, |
34 | } |
35 | |
36 | impl<'a> Variant<'a> { |
37 | pub fn as_name(&'a self) -> &'a str { |
38 | &self.name_in_attr |
39 | } |
40 | |
41 | pub fn as_unit_match_arm(&'a self) -> UnitMatchArm<'a> { |
42 | UnitMatchArm(self) |
43 | } |
44 | |
45 | pub fn as_data_match_arm(&'a self) -> DataMatchArm<'a> { |
46 | DataMatchArm(self) |
47 | } |
48 | } |
49 | |
50 | impl<'a> UsesTypeParams for Variant<'a> { |
51 | fn uses_type_params<'b>( |
52 | &self, |
53 | options: &usage::Options, |
54 | type_set: &'b IdentSet, |
55 | ) -> IdentRefSet<'b> { |
56 | self.data.uses_type_params(options, type_set) |
57 | } |
58 | } |
59 | |
60 | impl<'a> ToTokens for Variant<'a> { |
61 | fn to_tokens(&self, tokens: &mut TokenStream) { |
62 | if self.data.is_unit() { |
63 | self.as_unit_match_arm().to_tokens(tokens); |
64 | } else { |
65 | self.as_data_match_arm().to_tokens(tokens) |
66 | } |
67 | } |
68 | } |
69 | |
70 | /// Code generator for an enum variant in a unit match position. |
71 | /// This is placed in generated `from_string` calls for the parent enum. |
72 | /// Value-carrying variants wrapped in this type will emit code to produce an "unsupported format" error. |
73 | pub struct UnitMatchArm<'a>(&'a Variant<'a>); |
74 | |
75 | impl<'a> ToTokens for UnitMatchArm<'a> { |
76 | fn to_tokens(&self, tokens: &mut TokenStream) { |
77 | let val: &Variant<'a> = self.0; |
78 | |
79 | if val.skip { |
80 | return; |
81 | } |
82 | |
83 | let name_in_attr = &val.name_in_attr; |
84 | |
85 | let unsupported_format_error = || { |
86 | quote!(::darling::export::Err( |
87 | ::darling::Error::unsupported_format("literal" ) |
88 | )) |
89 | }; |
90 | |
91 | if val.data.is_unit() { |
92 | let variant_ident = val.variant_ident; |
93 | let ty_ident = val.ty_ident; |
94 | |
95 | tokens.append_all(quote!( |
96 | #name_in_attr => ::darling::export::Ok(#ty_ident::#variant_ident), |
97 | )); |
98 | } else if val.data.is_newtype() { |
99 | let field = val |
100 | .data |
101 | .fields |
102 | .first() |
103 | .expect("Newtype should have exactly one field" ); |
104 | let field_ty = field.ty; |
105 | let ty_ident = val.ty_ident; |
106 | let variant_ident = val.variant_ident; |
107 | let unsupported_format = unsupported_format_error(); |
108 | |
109 | tokens.append_all(quote!{ |
110 | #name_in_attr => { |
111 | match <#field_ty as ::darling::FromMeta>::from_none() { |
112 | ::darling::export::Some(__value) => ::darling::export::Ok(#ty_ident::#variant_ident(__value)), |
113 | ::darling::export::None => #unsupported_format, |
114 | } |
115 | } |
116 | }) |
117 | } else { |
118 | let unsupported_format = unsupported_format_error(); |
119 | tokens.append_all(quote!( |
120 | #name_in_attr => #unsupported_format, |
121 | )); |
122 | } |
123 | } |
124 | } |
125 | |
126 | /// Code generator for an enum variant in a data-carrying match position. |
127 | /// This is placed in generated `from_list` calls for the parent enum. |
128 | /// Unit variants wrapped in this type will emit code to produce an "unsupported format" error. |
129 | pub struct DataMatchArm<'a>(&'a Variant<'a>); |
130 | |
131 | impl<'a> ToTokens for DataMatchArm<'a> { |
132 | fn to_tokens(&self, tokens: &mut TokenStream) { |
133 | let val: &Variant<'a> = self.0; |
134 | |
135 | if val.skip { |
136 | return; |
137 | } |
138 | |
139 | let name_in_attr = &val.name_in_attr; |
140 | let variant_ident = val.variant_ident; |
141 | let ty_ident = val.ty_ident; |
142 | |
143 | if val.data.is_unit() { |
144 | tokens.append_all(quote!( |
145 | #name_in_attr => ::darling::export::Err(::darling::Error::unsupported_format("list" )), |
146 | )); |
147 | |
148 | return; |
149 | } |
150 | |
151 | let vdg = FieldsGen::new(&val.data, val.allow_unknown_fields); |
152 | |
153 | if val.data.is_struct() { |
154 | let declare_errors = ErrorDeclaration::default(); |
155 | let check_errors = ErrorCheck::with_location(name_in_attr); |
156 | let require_fields = vdg.require_fields(); |
157 | let decls = vdg.declarations(); |
158 | let core_loop = vdg.core_loop(); |
159 | let inits = vdg.initializers(); |
160 | |
161 | tokens.append_all(quote!( |
162 | #name_in_attr => { |
163 | if let ::darling::export::syn::Meta::List(ref __data) = *__nested { |
164 | let __items = ::darling::export::NestedMeta::parse_meta_list(__data.tokens.clone())?; |
165 | let __items = &__items; |
166 | |
167 | #declare_errors |
168 | |
169 | #decls |
170 | |
171 | #core_loop |
172 | |
173 | #require_fields |
174 | |
175 | #check_errors |
176 | |
177 | ::darling::export::Ok(#ty_ident::#variant_ident { |
178 | #inits |
179 | }) |
180 | } else { |
181 | ::darling::export::Err(::darling::Error::unsupported_format("non-list" )) |
182 | } |
183 | } |
184 | )); |
185 | } else if val.data.is_newtype() { |
186 | tokens.append_all(quote!( |
187 | #name_in_attr => { |
188 | ::darling::export::Ok( |
189 | #ty_ident::#variant_ident( |
190 | ::darling::FromMeta::from_meta(__nested) |
191 | .map_err(|e| e.at(#name_in_attr))?) |
192 | ) |
193 | } |
194 | )); |
195 | } else { |
196 | panic!("Match arms aren't supported for tuple variants yet" ); |
197 | } |
198 | } |
199 | } |
200 | |