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 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> { to_tokens(&self, tokens: &mut TokenStream)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