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