• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> {
as_name(&'a self) -> &'a str37     pub fn as_name(&'a self) -> &'a str {
38         &self.name_in_attr
39     }
40 
as_unit_match_arm(&'a self) -> UnitMatchArm<'a>41     pub fn as_unit_match_arm(&'a self) -> UnitMatchArm<'a> {
42         UnitMatchArm(self)
43     }
44 
as_data_match_arm(&'a self) -> DataMatchArm<'a>45     pub fn as_data_match_arm(&'a self) -> DataMatchArm<'a> {
46         DataMatchArm(self)
47     }
48 }
49 
50 impl<'a> UsesTypeParams for Variant<'a> {
uses_type_params<'b>( &self, options: &usage::Options, type_set: &'b IdentSet, ) -> IdentRefSet<'b>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> {
to_tokens(&self, tokens: &mut TokenStream)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> {
to_tokens(&self, tokens: &mut TokenStream)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         if val.data.is_unit() {
86             let variant_ident = val.variant_ident;
87             let ty_ident = val.ty_ident;
88 
89             tokens.append_all(quote!(
90                 #name_in_attr => ::darling::export::Ok(#ty_ident::#variant_ident),
91             ));
92         } else {
93             tokens.append_all(quote!(
94                 #name_in_attr => ::darling::export::Err(::darling::Error::unsupported_format("literal")),
95             ));
96         }
97     }
98 }
99 
100 /// Code generator for an enum variant in a data-carrying match position.
101 /// This is placed in generated `from_list` calls for the parent enum.
102 /// Unit variants wrapped in this type will emit code to produce an "unsupported format" error.
103 pub struct DataMatchArm<'a>(&'a Variant<'a>);
104 
105 impl<'a> ToTokens for DataMatchArm<'a> {
to_tokens(&self, tokens: &mut TokenStream)106     fn to_tokens(&self, tokens: &mut TokenStream) {
107         let val: &Variant<'a> = self.0;
108 
109         if val.skip {
110             return;
111         }
112 
113         let name_in_attr = &val.name_in_attr;
114         let variant_ident = val.variant_ident;
115         let ty_ident = val.ty_ident;
116 
117         if val.data.is_unit() {
118             tokens.append_all(quote!(
119                 #name_in_attr => ::darling::export::Err(::darling::Error::unsupported_format("list")),
120             ));
121 
122             return;
123         }
124 
125         let vdg = FieldsGen::new(&val.data, val.allow_unknown_fields);
126 
127         if val.data.is_struct() {
128             let declare_errors = ErrorDeclaration::default();
129             let check_errors = ErrorCheck::with_location(name_in_attr);
130             let require_fields = vdg.require_fields();
131             let decls = vdg.declarations();
132             let core_loop = vdg.core_loop();
133             let inits = vdg.initializers();
134 
135             tokens.append_all(quote!(
136                 #name_in_attr => {
137                     if let ::darling::export::syn::Meta::List(ref __data) = *__nested {
138                         let __items = ::darling::export::NestedMeta::parse_meta_list(__data.tokens.clone())?;
139                         let __items = &__items;
140 
141                         #declare_errors
142 
143                         #decls
144 
145                         #core_loop
146 
147                         #require_fields
148 
149                         #check_errors
150 
151                         ::darling::export::Ok(#ty_ident::#variant_ident {
152                             #inits
153                         })
154                     } else {
155                         ::darling::export::Err(::darling::Error::unsupported_format("non-list"))
156                     }
157                 }
158             ));
159         } else if val.data.is_newtype() {
160             tokens.append_all(quote!(
161                 #name_in_attr => {
162                     ::darling::export::Ok(
163                         #ty_ident::#variant_ident(
164                             ::darling::FromMeta::from_meta(__nested)
165                                 .map_err(|e| e.at(#name_in_attr))?)
166                     )
167                 }
168             ));
169         } else {
170             panic!("Match arms aren't supported for tuple variants yet");
171         }
172     }
173 }
174