1#![recursion_limit = "128"]
2extern crate proc_macro;
3extern crate syn;
4#[macro_use]
5extern crate quote;
6extern crate struct_diff;
7
8use proc_macro::TokenStream;
9
10#[proc_macro_derive(Diff)]
11pub fn generate_diff_impl(input: TokenStream) -> TokenStream {
12 let s = input.to_string();
13 let ast = syn::parse_derive_input(&s).unwrap();
15
16 let gen = impl_diff(&ast);
18
19 gen.parse().unwrap()
21}
22
23fn impl_diff(ast: &syn::DeriveInput) -> quote::Tokens {
24 let name = &ast.ident;
25 match ast.body {
26 syn::Body::Struct(ref vdata) => impl_diff_struct(name, &vdata),
27 syn::Body::Enum(ref variants) => impl_diff_enum(name, &variants),
28 }
29}
30
31struct StructGenerator<'a> {
33 fields: &'a [syn::Field]
34}
35
36impl<'a> quote::ToTokens for StructGenerator<'a> {
37 fn to_tokens(&self, tokens: &mut quote::Tokens) {
38 for field in self.fields.iter() {
39 if let Some(ref field_name) = field.ident {
40 let field_name_s = field_name.to_string();
41 tokens.append(
42 quote!{
43 if let Some(inner_diffs) = self.#field_name.diff(&other.#field_name) {
44 for diff in inner_diffs {
45 let mut path = String::from(#field_name_s);
46 if !diff.field.is_empty() {
47 path.push_str(&".");
48 }
49 path.push_str(&diff.field);
50 diffs.push(::struct_diff::Difference {
51 field: path,
52 left: diff.left,
53 right: diff.right,
54 })
55 }
56 }
57 }
58 )
59 }
60 }
61 }
62}
63
64struct FieldGenerator<'a> {
66 name: &'a syn::Ident,
67 fields: &'a [syn::Field],
68}
69
70impl<'a> quote::ToTokens for FieldGenerator<'a> {
71 fn to_tokens(&self, tokens: &mut quote::Tokens) {
72 for (i, field) in self.fields.iter().enumerate() {
73 let field_name = field.ident.clone().unwrap_or(syn::Ident::from(i));
74 let field_name_s = field_name.to_string();
75 let left = {
76 let mut new_name = String::from("left_");
77 new_name.push_str(&field_name.to_string());
78 syn::Ident::from(new_name)
79 };
80 let right = {
81 let mut new_name = String::from("right_");
82 new_name.push_str(&field_name.to_string());
83 syn::Ident::from(new_name)
84 };
85 let name = self.name.to_string();
86 tokens.append(
87 quote!{
88 if let Some(inner_diffs) = #left.diff(&#right) {
89 for diff in inner_diffs {
90 let mut path = String::from(#name);
91 path.push_str(".");
92 path.push_str(&#field_name_s);
93 if !diff.field.is_empty() {
94 path.push_str(&".");
95 }
96 path.push_str(&diff.field);
97 diffs.push(::struct_diff::Difference {
98 field: path,
99 left: diff.left,
100 right: diff.right,
101 })
102 }
103 }
104 }
105 )
106 }
107 }
108}
109
110struct StructPatField<'a> {
111 name: &'a syn::Ident,
112 prefix: &'static str,
113}
114
115impl<'a> quote::ToTokens for StructPatField<'a> {
116 fn to_tokens(&self, tokens: &mut quote::Tokens) {
117 let ident = self.name;
118 let prefixed_ident = {
119 let mut new_name = String::from(self.prefix);
120 new_name.push_str("_");
121 new_name.push_str(&ident.to_string());
122 syn::Ident::from(new_name)
123 };
124 tokens.append(quote! {
125 #ident: ref #prefixed_ident
126 })
127 }
128}
129
130fn impl_diff_enum(name: &syn::Ident, variants: &[syn::Variant]) -> quote::Tokens {
132 let mut differs = Vec::new();
133 for variant in variants {
134 let var_name = &variant.ident;
135 let var_data = &variant.data;
136 let diff = match var_data {
137 &syn::VariantData::Tuple(ref fields) => {
138 let gen = FieldGenerator { name: var_name, fields };
139 let names: Vec<syn::Ident> = fields.iter()
140 .enumerate()
141 .map(|(id, field)| field.ident.clone().unwrap_or(syn::Ident::from(id)))
142 .collect();
143 let left = names.iter().map(|ident| {
144 let mut new_name = String::from("left_");
145 new_name.push_str(&ident.to_string());
146 syn::Ident::from(new_name)
147 }).collect::<Vec<_>>();
148 let right = names.iter().map(|ident| {
149 let mut new_name = String::from("right_");
150 new_name.push_str(&ident.to_string());
151 syn::Ident::from(new_name)
152 }).collect::<Vec<_>>();
153 quote! {
154 (&#name::#var_name(#(ref #left),*), &#name::#var_name(#(ref #right),*)) => {
155 #gen
156 }
157 }
158 },
159 &syn::VariantData::Struct(ref fields) => {
160 let gen = FieldGenerator { name: var_name, fields };
161 let names: Vec<syn::Ident> = fields.iter()
162 .enumerate()
163 .map(|(id, field)| field.ident.clone().unwrap_or(syn::Ident::from(id)))
164 .collect();
165 let left = names.iter().map(|ident| {
166 StructPatField{ name: ident, prefix: "left"}
167 }).collect::<Vec<_>>();
168 let right = names.iter().map(|ident| {
169 StructPatField{ name: ident, prefix: "right"}
170 }).collect::<Vec<_>>();
171 quote! {
172 (&#name::#var_name{ #(#left),* }, &#name::#var_name{ #(#right),* }) => {
173 #gen
174 }
175 }
176 },
177 &syn::VariantData::Unit => {
178 quote! {
179 (&#name::#var_name, &#name::#var_name) => {
180 return None;
181 }
182 }
183 },
184 };
185 differs.push(diff);
186 }
187 return quote! {
188 impl ::struct_diff::Diff for #name {
189
190 #[allow(unreachable_patterns)]
191 fn diff<'a>(&'a self, other: &'a #name) -> Option<Vec<::struct_diff::Difference<'a>>> {
192 let mut diffs = Vec::with_capacity(1);
193 match (self, other) {
194 #(#differs),*
195 _ => {
196 diffs.push(::struct_diff::Difference { field: "self".into(), left: self, right: other });
197 }
198 }
199 if diffs.len() > 0 {
200 return Some(diffs);
201 }
202 None
203 }
204 }
205 }
206}
207
208struct TupleFieldsGenerator<'a> {
210 fields: &'a [syn::Field],
211}
212
213impl<'a> quote::ToTokens for TupleFieldsGenerator<'a> {
214 fn to_tokens(&self, tokens: &mut quote::Tokens) {
215 for (field_name, _) in self.fields.iter().enumerate() {
216 let field_name_s = field_name.to_string();
217 use quote::Ident;
218 let field_name = Ident::new(format!("{}", field_name));
219 tokens.append(
220 quote!{
221 if let Some(inner_diffs) = self.#field_name.diff(&other.#field_name) {
222 for diff in inner_diffs {
223 let mut path = String::from(#field_name_s);
224 if !diff.field.is_empty() {
225 path.push_str(&".");
226 }
227 path.push_str(&diff.field);
228 diffs.push(::struct_diff::Difference {
229 field: path,
230 left: diff.left,
231 right: diff.right,
232 })
233 }
234 }
235 }
236 );
237 }
238 }
239}
240
241fn impl_diff_struct(name: &syn::Ident, struct_: &syn::VariantData) -> quote::Tokens {
243 match struct_ {
244 &syn::VariantData::Struct(ref fields) => {
245 let gen = StructGenerator { fields };
246 return quote! {
247 impl ::struct_diff::Diff for #name {
248 fn diff<'a>(&'a self, other: &'a #name) -> Option<Vec<::struct_diff::Difference<'a>>> {
249 let mut diffs = Vec::new();
250 #gen
251 if diffs.len() > 0 {
252 return Some(diffs);
253 }
254 return None;
255 }
256 }
257 }
258 },
259 &syn::VariantData::Tuple(ref fields) => {
260 let gen = TupleFieldsGenerator { fields };
261 return quote! {
262 impl ::struct_diff::Diff for #name {
263 fn diff<'a>(&'a self, other: &'a #name) -> Option<Vec<::struct_diff::Difference<'a>>> {
264 let mut diffs = Vec::new();
265 #gen
266 if diffs.len() > 0 {
267 return Some(diffs);
268 }
269 return None;
270 }
271 }
272 }
273 },
274 v@_ => {
275 panic!("Support for {:?} not implemented", v);
277 }
278 }
279}