1 | use proc_macro2::TokenStream; |
2 | use quote::quote; |
3 | use syn::{Generics, Ident, WherePredicate}; |
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 bound: Option<&'a [WherePredicate]>, |
20 | pub allow_unknown_fields: bool, |
21 | } |
22 | |
23 | impl<'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 | |
79 | impl<'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 | |