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