darling_core/codegen/
variant.rs
1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens, TokenStreamExt};
5use syn::Ident;
6
7use crate::ast::Fields;
8use crate::codegen::error::{ErrorCheck, ErrorDeclaration};
9use crate::codegen::{Field, FieldsGen};
10use crate::usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
11
12#[derive(Debug, Clone)]
14pub struct Variant<'a> {
15 pub name_in_attr: Cow<'a, String>,
17
18 pub variant_ident: &'a Ident,
20
21 pub ty_ident: &'a Ident,
23
24 pub data: Fields<Field<'a>>,
25
26 pub skip: bool,
28
29 pub word: bool,
32
33 pub allow_unknown_fields: bool,
34}
35
36impl<'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
50impl<'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
60impl<'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
70pub struct UnitMatchArm<'a>(&'a Variant<'a>);
74
75impl<'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
126pub struct DataMatchArm<'a>(&'a Variant<'a>);
130
131impl<'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}