embedded_layout_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::{
7    self, parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, GenericParam,
8    LitInt, TypeParamBound,
9};
10
11#[proc_macro_derive(ViewGroup)]
12pub fn derive_viewgroup(input: TokenStream) -> TokenStream {
13    let ast = parse_macro_input!(input as DeriveInput);
14
15    let empty_vg_instance = quote!(unsafe { &embedded_layout::view_group::EMPTY_VIEW_GROUP });
16    let empty_vg_instance_mut =
17        quote!(unsafe { &mut embedded_layout::view_group::EMPTY_VIEW_GROUP });
18
19    let (
20        field_count_impl,
21        index_impl,
22        index_mut_impl,
23        translate_impl,
24        draw_impl,
25        bounds_of_impl,
26        translate_child_impl,
27    ) = match &ast.data {
28        Data::Struct(struct_data) if matches!(&struct_data.fields, Fields::Named(_)) => {
29            let fields = if let Fields::Named(fields) = &struct_data.fields {
30                fields
31            } else {
32                panic!("Programming error: matches! should have prevent from taking this arm");
33            };
34
35            let field_names = fields
36                .named
37                .iter()
38                .map(|f| f.ident.clone().unwrap())
39                .collect::<Vec<_>>();
40
41            let field_count = format!("{}", field_names.len());
42            let field_count = LitInt::new(&field_count, Span::call_site());
43
44            let translate = field_names
45                .iter()
46                .map(|f| quote!(#f: self.#f.clone().translate(by),))
47                .collect::<Vec<_>>();
48
49            let draw = field_names
50                .iter()
51                .map(|f| quote!(self.#f.draw(display)?;))
52                .collect::<Vec<_>>();
53
54            let index = field_names
55                .iter()
56                .enumerate()
57                .map(|(i, f)| quote!(#i => &self.#f,))
58                .collect::<Vec<_>>();
59
60            let index_mut = field_names
61                .iter()
62                .enumerate()
63                .map(|(i, f)| quote!(#i => &mut self.#f,))
64                .collect::<Vec<_>>();
65
66            let bounds_of = field_names
67                .iter()
68                .enumerate()
69                .map(|(i, f)| quote!(#i => self.#f.bounds(),))
70                .collect::<Vec<_>>();
71
72            let translate_child = field_names
73                .iter()
74                .enumerate()
75                .map(|(i, f)| quote!(#i => self.#f.translate_impl(by),))
76                .collect::<Vec<_>>();
77
78            let field_count_impl = quote! {
79                #field_count
80            };
81
82            let index_impl = quote! {
83                match index {
84                    #(#index)*
85                    _ => #empty_vg_instance
86                }
87            };
88
89            let index_mut_impl = quote! {
90                match index {
91                    #(#index_mut)*
92                    _ => #empty_vg_instance_mut
93                }
94            };
95
96            let translate_impl = quote! {
97                Self {
98                    #(#translate)*
99                }
100            };
101
102            let bounds_of_impl = quote! {
103                match index {
104                    #(#bounds_of)*
105                    _ => embedded_graphics::primitives::Rectangle::zero(),
106                }
107            };
108
109            let translate_child_impl = quote! {
110                match index {
111                    #(#translate_child)*
112                    _ => {}
113                }
114            };
115
116            let draw_impl = quote! {
117                #(#draw)*
118            };
119
120            (
121                field_count_impl,
122                index_impl,
123                index_mut_impl,
124                translate_impl,
125                draw_impl,
126                bounds_of_impl,
127                translate_child_impl,
128            )
129        }
130        Data::Enum(enum_data) => {
131            let mut enum_field_counts = Vec::new();
132            let mut enum_translates = Vec::new();
133            let mut enum_indexes = Vec::new();
134            let mut enum_mut_indexes = Vec::new();
135            let mut enum_draws = Vec::new();
136            let mut enum_bounds_ofs = Vec::new();
137            let mut enum_translate_childs = Vec::new();
138
139            enum_data.variants.iter().for_each(|variant| {
140                let variant_name = &variant.ident;
141
142                let (
143                    enum_field_count,
144                    enum_translate,
145                    enum_index,
146                    enum_mut_index,
147                    enum_draw,
148                    enum_bounds_of,
149                    enum_translate_child,
150                ) = match &variant.fields {
151                    Fields::Named(FieldsNamed { named, .. }) => {
152                        let field_idents = named
153                            .iter()
154                            .map(|field| field.ident.as_ref().unwrap())
155                            .collect::<Vec<_>>();
156
157                        let fields_count = named.iter().count();
158                        let enum_field_count = quote! {
159                            Self::#variant_name { ..  } => {
160                                #fields_count
161                            }
162                        };
163
164                        let translate_fields = field_idents
165                            .iter()
166                            .map(|f| quote!(#f: #f.clone().translate(by)));
167                        let enum_translate = quote! {
168                            Self::#variant_name { #(#field_idents,)* } => {
169                                Self::#variant_name {
170                                    #(#translate_fields,)*
171                                }
172                            }
173                        };
174
175                        let fields_index = field_idents
176                            .iter()
177                            .enumerate()
178                            .map(|(i, f)| quote!(#i => #f,))
179                            .collect::<Vec<_>>();
180                        let enum_index = quote! {
181                            Self::#variant_name { #(#field_idents,)* } => {
182                                match index {
183                                    #(#fields_index)*
184                                    _ => #empty_vg_instance,
185                                }
186                            }
187                        };
188                        let enum_mut_index = quote! {
189                            Self::#variant_name { #(#field_idents,)* } => {
190                                match index {
191                                    #(#fields_index)*
192                                    _ => #empty_vg_instance_mut,
193                                }
194                            }
195                        };
196
197                        let fields_draw = field_idents.iter().map(|f| quote!(#f.draw(display)?;));
198                        let enum_draw = quote! {
199                            Self::#variant_name { #(#field_idents,)* } => {
200                                #(#fields_draw)*
201                            }
202                        };
203
204                        let fields_bounds_of = field_idents
205                            .iter()
206                            .enumerate()
207                            .map(|(i, f)| quote!(#i => #f.bounds(),));
208                        let enum_bounds_of = quote! {
209                            Self::#variant_name { #(#field_idents,)* } => {
210                                match index {
211                                    #(#fields_bounds_of)*
212                                    _ => embedded_graphics::primitives::Rectangle::zero(),
213                                }
214                            }
215                        };
216
217                        let fields_translate_child = field_idents
218                            .iter()
219                            .enumerate()
220                            .map(|(i, f)| quote!(#i => #f.translate_impl(by),));
221                        let enum_translate_child = quote! {
222                            Self::#variant_name { #(#field_idents,)* } => {
223                                match index {
224                                    #(#fields_translate_child)*
225                                    _ => {},
226                                }
227                            }
228                        };
229
230                        (
231                            enum_field_count,
232                            enum_translate,
233                            enum_index,
234                            enum_mut_index,
235                            enum_draw,
236                            enum_bounds_of,
237                            enum_translate_child,
238                        )
239                    }
240                    Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
241                        let field_idents = unnamed
242                            .iter()
243                            .enumerate()
244                            .map(|(num, _)| format_ident!("__self_{}", num))
245                            .collect::<Vec<_>>();
246
247                        let fields_count = unnamed.iter().count();
248                        let enum_field_count = quote! {
249                            Self::#variant_name(..) => {
250                                #fields_count
251                            }
252                        };
253
254                        let translate_fields = field_idents
255                            .iter()
256                            .map(|f| quote!(#f.clone().translate(by), ));
257                        let enum_translate = quote! {
258                            Self::#variant_name(#(#field_idents),*) => {
259                                Self::#variant_name(
260                                    #(#translate_fields)*
261                                )
262                            }
263                        };
264
265                        let fields_index = field_idents
266                            .iter()
267                            .enumerate()
268                            .map(|(i, f)| quote!(#i => #f,))
269                            .collect::<Vec<_>>();
270                        let enum_index = quote! {
271                            Self::#variant_name(#(#field_idents),*) => {
272                                match index {
273                                    #(#fields_index)*
274                                    _ => #empty_vg_instance,
275                                }
276                            }
277                        };
278
279                        let enum_mut_index = quote! {
280                            Self::#variant_name(#(#field_idents),*) => {
281                                match index {
282                                    #(#fields_index)*
283                                    _ => #empty_vg_instance_mut,
284                                }
285                            }
286                        };
287
288                        let field_draws = field_idents.iter().map(|f| quote!(#f.draw(display)?;));
289                        let enum_draw = quote! {
290                            Self::#variant_name(#(#field_idents),*) => {
291                                #(#field_draws)*
292                            }
293                        };
294
295                        let fields_bounds_of = field_idents
296                            .iter()
297                            .enumerate()
298                            .map(|(i, f)| quote!(#i => #f.bounds(),));
299                        let enum_bounds_of = quote! {
300                            Self::#variant_name(#(#field_idents),*) => {
301                                match index {
302                                    #(#fields_bounds_of)*
303                                    _ => embedded_graphics::primitives::Rectangle::zero(),
304                                }
305                            }
306                        };
307
308                        let fields_translate_child = field_idents
309                            .iter()
310                            .enumerate()
311                            .map(|(i, f)| quote!(#i => #f.translate_impl(by),));
312                        let enum_translate_child = quote! {
313                            Self::#variant_name(#(#field_idents),*) => {
314                                match index {
315                                    #(#fields_translate_child)*
316                                    _ => {},
317                                }
318                            }
319                        };
320
321                        (
322                            enum_field_count,
323                            enum_translate,
324                            enum_index,
325                            enum_mut_index,
326                            enum_draw,
327                            enum_bounds_of,
328                            enum_translate_child,
329                        )
330                    }
331                    Fields::Unit => {
332                        let enum_field_count = quote! {
333                            Self::#variant_name => 0,
334                        };
335                        let enum_translate = quote! {
336                            Self::#variant_name => Self::#variant_name,
337                        };
338                        let enum_index = quote! {
339                            Self::#variant_name => {
340                                #empty_vg_instance
341                            }
342                        };
343
344                        let enum_mut_index = quote! {
345                            Self::#variant_name => {
346                                #empty_vg_instance_mut
347                            }
348                        };
349                        let enum_draw = quote! {
350                            Self::#variant_name => {}
351                        };
352
353                        let enum_bounds_of = quote! {
354                            Self::#variant_name => {
355                                embedded_graphics::primitives::Rectangle::zero()
356                            }
357                        };
358
359                        let enum_translate_child = quote! {
360                            Self::#variant_name => {}
361                        };
362
363                        (
364                            enum_field_count,
365                            enum_translate,
366                            enum_index,
367                            enum_mut_index,
368                            enum_draw,
369                            enum_bounds_of,
370                            enum_translate_child,
371                        )
372                    }
373                };
374
375                enum_field_counts.push(enum_field_count);
376                enum_translates.push(enum_translate);
377                enum_indexes.push(enum_index);
378                enum_mut_indexes.push(enum_mut_index);
379                enum_draws.push(enum_draw);
380                enum_bounds_ofs.push(enum_bounds_of);
381                enum_translate_childs.push(enum_translate_child);
382            });
383
384            let field_count_impl = quote! {
385                match self {
386                    #(#enum_field_counts)*
387                }
388            };
389
390            let index_impl = quote! {
391                match self {
392                    #(#enum_indexes)*
393                }
394            };
395
396            let index_mut_impl = quote! {
397                match self {
398                    #(#enum_mut_indexes)*
399                }
400            };
401
402            let translate_impl = quote! {
403                match self {
404                    #(#enum_translates)*
405                }
406            };
407
408            let draw_impl = quote! {
409                match self {
410                    #(#enum_draws)*
411                }
412            };
413
414            let bounds_of_impl = quote! {
415                match self {
416                    #(#enum_bounds_ofs)*
417                }
418            };
419
420            let translate_child_impl = quote! {
421                match self {
422                    #(#enum_translate_childs)*
423                }
424            };
425
426            (
427                field_count_impl,
428                index_impl,
429                index_mut_impl,
430                translate_impl,
431                draw_impl,
432                bounds_of_impl,
433                translate_child_impl,
434            )
435        }
436        _ => panic!("derive(ViewGroup) only supports structs with named fields and enums"),
437    };
438
439    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
440
441    let name = &ast.ident;
442
443    let gen_view_group = quote! {
444        impl #impl_generics embedded_layout::view_group::ViewGroup for #name #ty_generics #where_clause {
445            #[inline]
446            fn len(&self) -> usize {
447                #field_count_impl
448            }
449
450            #[inline]
451            fn at(&self, index: usize) -> &dyn embedded_layout::View {
452                #index_impl
453            }
454
455            #[inline]
456            fn at_mut(&mut self, index: usize) -> &mut dyn embedded_layout::View {
457                #index_mut_impl
458            }
459
460            #[inline]
461            fn bounds_of(&self, index: usize) -> embedded_graphics::primitives::Rectangle {
462                use embedded_layout::View;
463                #bounds_of_impl
464            }
465
466            #[inline]
467            fn translate_child(&mut self, index: usize, by: Point) {
468                use embedded_layout::View;
469                #translate_child_impl
470            }
471        }
472
473        impl #impl_generics embedded_graphics::transform::Transform for #name #ty_generics #where_clause {
474            #[inline]
475            fn translate(&self, by: Point) -> Self {
476                #translate_impl
477            }
478
479            #[inline]
480            fn translate_mut(&mut self, by: Point) -> &mut Self {
481                embedded_layout::view_group::ViewGroupHelper::translate(self, by);
482                self
483            }
484        }
485
486        impl #impl_generics embedded_graphics::geometry::Dimensions for #name #ty_generics #where_clause {
487            #[inline]
488            fn bounding_box(&self) -> embedded_graphics::primitives::Rectangle {
489                embedded_layout::view_group::ViewGroupHelper::bounds(self)
490            }
491        }
492    };
493
494    let pixelcolor = ast.generics.params.iter().find_map(|p| {
495        if let GenericParam::Type(tp) = p {
496            if tp.bounds.iter().any(is_type_param("PixelColor")) {
497                Some(tp.ident.clone())
498            } else {
499                None
500            }
501        } else {
502            None
503        }
504    });
505
506    let gen_drawable_impl = if let Some(pixelcolor) = pixelcolor {
507        quote! {
508            impl #impl_generics embedded_graphics::Drawable for #name #ty_generics #where_clause {
509                type Color = #pixelcolor;
510                type Output = ();
511
512                #[inline]
513                fn draw<D: embedded_graphics::draw_target::DrawTarget<Color = #pixelcolor>>(&self, display: &mut D) -> Result<(), D::Error> {
514                    #draw_impl
515
516                    Ok(())
517                }
518            }
519        }
520    } else {
521        quote!()
522    };
523
524    let generated = quote! {
525        #gen_view_group
526        #gen_drawable_impl
527    };
528
529    TokenStream::from(generated)
530}
531
532fn is_type_param(ident: &'static str) -> impl Fn(&TypeParamBound) -> bool {
533    move |f: &TypeParamBound| -> bool {
534        if let TypeParamBound::Trait(tpb) = f {
535            tpb.path
536                .segments
537                .iter()
538                .last()
539                .map(|segment| segment.ident == ident)
540                .unwrap_or(false)
541        } else {
542            false
543        }
544    }
545}