use proc_macro2::TokenStream; use quote::quote; use syn::{Generics, Ident, WherePredicate}; use crate::ast::{Data, Fields}; use crate::codegen::{ error::{ErrorCheck, ErrorDeclaration}, DefaultExpression, Field, FieldsGen, PostfixTransform, Variant, }; use crate::usage::{CollectTypeParams, IdentSet, Purpose}; #[derive(Debug)] pub struct TraitImpl<'a> { pub ident: &'a Ident, pub generics: &'a Generics, pub data: Data, Field<'a>>, pub default: Option>, pub post_transform: Option<&'a PostfixTransform>, pub bound: Option<&'a [WherePredicate]>, pub allow_unknown_fields: bool, } impl<'a> TraitImpl<'a> { /// Get all declared type parameters. pub fn declared_type_params(&self) -> IdentSet { self.generics .type_params() .map(|tp| tp.ident.clone()) .collect() } /// Get the type parameters which are used by non-skipped, non-magic fields. /// These type parameters will have a `FromMeta` bound applied to them in emitted /// code. pub fn used_type_params(&self) -> IdentSet { self.type_params_matching(|f| !f.skip, |v| !v.skip) } fn type_params_matching(&self, field_filter: F, variant_filter: V) -> IdentSet where F: Fn(&&Field) -> bool, V: Fn(&&Variant) -> bool, { let declared = self.declared_type_params(); match self.data { Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared), Data::Enum(ref v) => { v.iter() .filter(variant_filter) .fold(Default::default(), |mut state, variant| { state.extend(self.type_params_in_fields( &variant.data, &field_filter, &declared, )); state }) } } } /// Get the type parameters of all fields in a set matching some filter fn type_params_in_fields<'b, F>( &'b self, fields: &'b Fields>, field_filter: F, declared: &IdentSet, ) -> IdentSet where F: Fn(&&'b Field) -> bool, { fields .iter() .filter(field_filter) .collect_type_params_cloned(&Purpose::BoundImpl.into(), declared) } } impl<'a> TraitImpl<'a> { /// Gets the `let` declaration for errors accumulated during parsing. pub fn declare_errors(&self) -> ErrorDeclaration { ErrorDeclaration::default() } /// Gets the check which performs an early return if errors occurred during parsing. pub fn check_errors(&self) -> ErrorCheck { ErrorCheck::default() } /// Generate local variable declarations for all fields. pub(in crate::codegen) fn local_declarations(&self) -> TokenStream { if let Data::Struct(ref vd) = self.data { let vdr = vd.as_ref().map(Field::as_declaration); let decls = vdr.fields.as_slice(); quote!(#(#decls)*) } else { quote!() } } pub(in crate::codegen) fn post_transform_call(&self) -> Option { self.post_transform.map(|pt| quote!(#pt)) } /// Generate local variable declaration and initialization for instance from which missing fields will be taken. pub(in crate::codegen) fn fallback_decl(&self) -> TokenStream { let default = self.default.as_ref().map(DefaultExpression::as_declaration); quote!(#default) } pub fn require_fields(&self) -> TokenStream { if let Data::Struct(ref vd) = self.data { let check_nones = vd.as_ref().map(Field::as_presence_check); let checks = check_nones.fields.as_slice(); quote!(#(#checks)*) } else { quote!() } } pub(in crate::codegen) fn initializers(&self) -> TokenStream { self.make_field_ctx().initializers() } /// Generate the loop which walks meta items looking for property matches. pub(in crate::codegen) fn core_loop(&self) -> TokenStream { self.make_field_ctx().core_loop() } fn make_field_ctx(&'a self) -> FieldsGen<'a> { match self.data { Data::Enum(_) => panic!("Core loop on enums isn't supported"), Data::Struct(ref data) => FieldsGen::new(data, self.allow_unknown_fields), } } }