1extern crate proc_macro;
10
11use quote::quote;
12
13mod generate;
14mod parse;
15
16use crate::{
17 generate::{FieldType, GetType},
18 parse::{FieldDef, GetTypeConf, PropertyDef, SetTypeConf},
19};
20
21#[proc_macro_derive(Property, attributes(property))]
23pub fn derive_property(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
24 let property = syn::parse_macro_input!(input as PropertyDef);
25 let expanded = {
26 let name = &property.name;
27 let (impl_generics, type_generics, where_clause_opt) = property.generics.split_for_impl();
28 let methods = property.fields.iter().fold(Vec::new(), |mut r, f| {
29 if !f.conf.skip {
30 r.append(&mut derive_property_for_field(f));
31 }
32 r
33 });
34 let impl_methods = quote!(
35 impl #impl_generics #name #type_generics #where_clause_opt {
36 #(#[inline] #methods)*
37 }
38 );
39 if let Some(impl_traits) = implement_traits(&property) {
40 quote!(#impl_methods #impl_traits)
41 } else {
42 impl_methods
43 }
44 };
45 expanded.into()
46}
47
48fn implement_traits(property: &PropertyDef) -> Option<proc_macro2::TokenStream> {
49 let name = &property.name;
50 let mut ordered: Vec<_> = property
51 .fields
52 .iter()
53 .filter(|f| f.conf.ord.number.is_some())
54 .collect();
55 if ordered.is_empty() {
56 None
57 } else {
58 ordered.sort_by(|f1, f2| {
59 let n1 = f1.conf.ord.number.unwrap();
60 let n2 = f2.conf.ord.number.unwrap();
61 n1.cmp(&n2)
62 });
63 let has_same_serial_number = ordered.windows(2).any(|f| {
64 let n1 = f[0].conf.ord.number.unwrap();
65 let n2 = f[1].conf.ord.number.unwrap();
66 n1 == n2
67 });
68 if has_same_serial_number {
69 panic!("there are at least two fields that have same serial number");
70 }
71 let partial_eq_stmt = ordered.iter().fold(Vec::new(), |mut r, f| {
72 if !r.is_empty() {
73 r.push(quote!(&&));
74 }
75 let field_name = &f.ident;
76 r.push(quote!(self.#field_name == other.#field_name));
77 r
78 });
79 let partial_ord_stmt = ordered.iter().fold(Vec::new(), |mut r, f| {
80 let field_name = &f.ident;
81 r.push(if f.conf.ord.sort_type.is_ascending() {
82 quote!(let result = self.#field_name.partial_cmp(&other.#field_name);)
83 } else {
84 quote!(let result = other.#field_name.partial_cmp(&self.#field_name);)
85 });
86 r.push(quote!(if result != Some(::core::cmp::Ordering::Equal) {
87 return result;
88 }));
89 r
90 });
91 let stmts = quote!(
92 impl PartialEq for #name {
93 fn eq(&self, other: &Self) -> bool {
94 #(#partial_eq_stmt)*
95 }
96 }
97
98 impl PartialOrd for #name {
99 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
100 #(#partial_ord_stmt)*
101 Some(::core::cmp::Ordering::Equal)
102 }
103 }
104 );
105 Some(stmts)
106 }
107}
108
109fn derive_property_for_field(field: &FieldDef) -> Vec<proc_macro2::TokenStream> {
110 let mut property = Vec::new();
111 let field_type = &field.ty;
112 let field_name = &field.ident;
113 let field_conf = &field.conf;
114 let prop_field_type = FieldType::from_type(field_type);
115 if let Some(ts) = field_conf.get.vis.to_ts().map(|visibility| {
116 let method_name = field_conf.get.name.complete(field_name);
117 let get_type = match field_conf.get.typ {
118 GetTypeConf::NotSet => GetType::from_field_type(&prop_field_type),
119 GetTypeConf::Ref => GetType::Ref,
120 GetTypeConf::Copy_ => GetType::Copy_,
121 GetTypeConf::Clone_ => GetType::Clone_,
122 };
123 match get_type {
124 GetType::Ref => quote!(
125 #visibility fn #method_name(&self) -> &#field_type {
126 &self.#field_name
127 }
128 ),
129 GetType::Copy_ => quote!(
130 #visibility fn #method_name(&self) -> #field_type {
131 self.#field_name
132 }
133 ),
134 GetType::Clone_ => quote!(
135 #visibility fn #method_name(&self) -> #field_type {
136 self.#field_name.clone()
137 }
138 ),
139 GetType::String_ => quote!(
140 #visibility fn #method_name(&self) -> &str {
141 &self.#field_name[..]
142 }
143 ),
144 GetType::Slice(field_type) => quote!(
145 #visibility fn #method_name(&self) -> &#field_type {
146 &self.#field_name[..]
147 }
148 ),
149 GetType::Option_(field_type) => quote!(
150 #visibility fn #method_name(&self) -> Option<&#field_type> {
151 self.#field_name.as_ref()
152 }
153 ),
154 }
155 }) {
156 property.push(ts);
157 }
158 if let Some(ts) = field_conf.set.vis.to_ts().map(|visibility| {
159 let method_name = field_conf.set.name.complete(field_name);
160 match prop_field_type {
161 FieldType::Vector(inner_type) => match field_conf.set.typ {
162 SetTypeConf::Ref => quote!(
163 #visibility fn #method_name<T: Into<#inner_type>>(
164 &mut self,
165 val: impl IntoIterator<Item = T>
166 ) -> &mut Self {
167 self.#field_name = val.into_iter().map(Into::into).collect();
168 self
169 }
170 ),
171 SetTypeConf::Own => quote!(
172 #visibility fn #method_name<T: Into<#inner_type>>(
173 mut self,
174 val: impl IntoIterator<Item = T>
175 ) -> Self {
176 self.#field_name = val.into_iter().map(Into::into).collect();
177 self
178 }
179 ),
180 SetTypeConf::None_ => quote!(
181 #visibility fn #method_name<T: Into<#inner_type>>(
182 &mut self,
183 val: impl IntoIterator<Item = T>
184 ) {
185 self.#field_name = val.into_iter().map(Into::into).collect();
186 }
187 ),
188 SetTypeConf::Replace => quote!(
189 #visibility fn #method_name<T: Into<#inner_type>>(
190 &mut self,
191 val: impl IntoIterator<Item = T>
192 ) -> #field_type {
193 ::core::mem::replace(&mut self.#field_name, val.into_iter().map(Into::into).collect())
194 }
195 ),
196 },
197 _ => match field_conf.set.typ {
198 SetTypeConf::Ref => quote!(
199 #visibility fn #method_name<T: Into<#field_type>>(
200 &mut self, val: T
201 ) -> &mut Self {
202 self.#field_name = val.into();
203 self
204 }
205 ),
206 SetTypeConf::Own => quote!(
207 #visibility fn #method_name<T: Into<#field_type>>(
208 mut self, val: T
209 ) -> Self {
210 self.#field_name = val.into();
211 self
212 }
213 ),
214 SetTypeConf::None_ => quote!(
215 #visibility fn #method_name<T: Into<#field_type>>(
216 &mut self, val: T
217 ) {
218 self.#field_name = val.into();
219 }
220 ),
221 SetTypeConf::Replace => quote!(
222 #visibility fn #method_name<T: Into<#field_type>>(
223 &mut self, val: T
224 ) -> #field_type {
225 ::core::mem::replace(&mut self.#field_name, val.into())
226 }
227 ),
228 },
229 }
230 }) {
231 property.push(ts);
232 }
233 if let Some(ts) = field_conf.mut_.vis.to_ts().map(|visibility| {
234 let method_name = field_conf.mut_.name.complete(field_name);
235 quote!(
236 #visibility fn #method_name(&mut self) -> &mut #field_type {
237 &mut self.#field_name
238 }
239 )
240 }) {
241 property.push(ts);
242 }
243 property
244}