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 allow_unknown_fields: bool,
30}
31
32impl<'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
46impl 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
56pub struct UnitMatchArm<'a>(&'a Variant<'a>);
60
61impl 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
112pub struct DataMatchArm<'a>(&'a Variant<'a>);
116
117impl 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 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}