1 use proc_macro2::TokenStream; 2 use quote::quote; 3 use syn::{Generics, Ident}; 4 5 use crate::ast::{Data, Fields}; 6 use crate::codegen::{ 7 error::{ErrorCheck, ErrorDeclaration}, 8 DefaultExpression, Field, FieldsGen, PostfixTransform, Variant, 9 }; 10 use crate::usage::{CollectTypeParams, IdentSet, Purpose}; 11 12 #[derive(Debug)] 13 pub struct TraitImpl<'a> { 14 pub ident: &'a Ident, 15 pub generics: &'a Generics, 16 pub data: Data<Variant<'a>, Field<'a>>, 17 pub default: Option<DefaultExpression<'a>>, 18 pub post_transform: Option<&'a PostfixTransform>, 19 pub allow_unknown_fields: bool, 20 } 21 22 impl<'a> TraitImpl<'a> { 23 /// Get all declared type parameters. declared_type_params(&self) -> IdentSet24 pub fn declared_type_params(&self) -> IdentSet { 25 self.generics 26 .type_params() 27 .map(|tp| tp.ident.clone()) 28 .collect() 29 } 30 31 /// Get the type parameters which are used by non-skipped, non-magic fields. 32 /// These type parameters will have a `FromMeta` bound applied to them in emitted 33 /// code. used_type_params(&self) -> IdentSet34 pub fn used_type_params(&self) -> IdentSet { 35 self.type_params_matching(|f| !f.skip, |v| !v.skip) 36 } 37 type_params_matching<F, V>(&self, field_filter: F, variant_filter: V) -> IdentSet where F: Fn(&&Field<'_>) -> bool, V: Fn(&&Variant<'_>) -> bool,38 fn type_params_matching<F, V>(&self, field_filter: F, variant_filter: V) -> IdentSet 39 where 40 F: Fn(&&Field<'_>) -> bool, 41 V: Fn(&&Variant<'_>) -> bool, 42 { 43 let declared = self.declared_type_params(); 44 match self.data { 45 Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared), 46 Data::Enum(ref v) => { 47 v.iter() 48 .filter(variant_filter) 49 .fold(Default::default(), |mut state, variant| { 50 state.extend(self.type_params_in_fields( 51 &variant.data, 52 &field_filter, 53 &declared, 54 )); 55 state 56 }) 57 } 58 } 59 } 60 61 /// Get the type parameters of all fields in a set matching some filter type_params_in_fields<'b, F>( &'b self, fields: &'b Fields<Field<'a>>, field_filter: F, declared: &IdentSet, ) -> IdentSet where F: Fn(&&'b Field<'_>) -> bool,62 fn type_params_in_fields<'b, F>( 63 &'b self, 64 fields: &'b Fields<Field<'a>>, 65 field_filter: F, 66 declared: &IdentSet, 67 ) -> IdentSet 68 where 69 F: Fn(&&'b Field<'_>) -> bool, 70 { 71 fields 72 .iter() 73 .filter(field_filter) 74 .collect_type_params_cloned(&Purpose::BoundImpl.into(), declared) 75 } 76 } 77 78 impl<'a> TraitImpl<'a> { 79 /// Gets the `let` declaration for errors accumulated during parsing. declare_errors(&self) -> ErrorDeclaration80 pub fn declare_errors(&self) -> ErrorDeclaration { 81 ErrorDeclaration::default() 82 } 83 84 /// Gets the check which performs an early return if errors occurred during parsing. check_errors(&self) -> ErrorCheck<'_>85 pub fn check_errors(&self) -> ErrorCheck<'_> { 86 ErrorCheck::default() 87 } 88 89 /// Generate local variable declarations for all fields. local_declarations(&self) -> TokenStream90 pub(in crate::codegen) fn local_declarations(&self) -> TokenStream { 91 if let Data::Struct(ref vd) = self.data { 92 let vdr = vd.as_ref().map(Field::as_declaration); 93 let decls = vdr.fields.as_slice(); 94 quote!(#(#decls)*) 95 } else { 96 quote!() 97 } 98 } 99 post_transform_call(&self) -> Option<TokenStream>100 pub(in crate::codegen) fn post_transform_call(&self) -> Option<TokenStream> { 101 self.post_transform.map(|pt| quote!(#pt)) 102 } 103 104 /// Generate local variable declaration and initialization for instance from which missing fields will be taken. fallback_decl(&self) -> TokenStream105 pub(in crate::codegen) fn fallback_decl(&self) -> TokenStream { 106 let default = self.default.as_ref().map(DefaultExpression::as_declaration); 107 quote!(#default) 108 } 109 require_fields(&self) -> TokenStream110 pub fn require_fields(&self) -> TokenStream { 111 if let Data::Struct(ref vd) = self.data { 112 let check_nones = vd.as_ref().map(Field::as_presence_check); 113 let checks = check_nones.fields.as_slice(); 114 115 // If a field was marked `flatten`, now is the time to process any unclaimed meta items 116 // and mark the field as having been seen. 117 let flatten_field_init = vd.fields.iter().find(|f| f.flatten).map(|v| { 118 v.as_flatten_initializer(vd.fields.iter().filter_map(Field::as_name).collect()) 119 }); 120 121 quote! { 122 #flatten_field_init 123 #(#checks)* 124 } 125 } else { 126 quote!() 127 } 128 } 129 initializers(&self) -> TokenStream130 pub(in crate::codegen) fn initializers(&self) -> TokenStream { 131 self.make_field_ctx().initializers() 132 } 133 134 /// Generate the loop which walks meta items looking for property matches. core_loop(&self) -> TokenStream135 pub(in crate::codegen) fn core_loop(&self) -> TokenStream { 136 self.make_field_ctx().core_loop() 137 } 138 make_field_ctx(&'a self) -> FieldsGen<'a>139 fn make_field_ctx(&'a self) -> FieldsGen<'a> { 140 match self.data { 141 Data::Enum(_) => panic!("Core loop on enums isn't supported"), 142 Data::Struct(ref data) => FieldsGen::new(data, self.allow_unknown_fields), 143 } 144 } 145 } 146