red_sdl_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields};
6
7#[allow(clippy::too_many_lines)]
8#[proc_macro_derive(UserControl, attributes(parent, state, child, childSelf))]
9pub fn user_control_derive(input: TokenStream) -> TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let name = &input.ident;
12    let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
13
14    let mut parent = parse_quote!(());
15    let mut state = parse_quote!(());
16    let mut child_field = None;
17
18    for attr in &input.attrs {
19        if attr.path().is_ident("parent") {
20            attr.parse_nested_meta(|meta| {
21                parent = meta.path.to_token_stream();
22                Ok(())
23            })
24            .expect("Error parsing parent type attribute");
25        } else if attr.path().is_ident("state") {
26            attr.parse_nested_meta(|meta| {
27                state = meta.path.to_token_stream();
28                Ok(())
29            })
30            .expect("Error parsing state type attribute");
31        }
32    }
33
34    let expanded = match &input.data {
35        Data::Enum(data_enum) => {
36            let variants = data_enum.variants.iter().map(|variant| {
37                let variant_name = &variant.ident;
38                match &variant.fields {
39                    Fields::Unnamed(fields) => {
40                        if fields.unnamed.len() != 1{
41                            panic!("Multiple element in enum case");
42                        }
43                        let field_type = fields.unnamed.first().expect("").ty.to_token_stream();
44                        (quote! {
45                            #name::#variant_name(el) => UserControl::surface(el.into(), parent, state),
46                        },quote! {
47                            #name::#variant_name(el) => UserControl::event(el.into(), canvas, event, parent, state),
48                        },quote! {
49                            #name::#variant_name(el) => UserControl::update(el.into(), canvas, elapsed, parent, state),
50                        },quote! {
51                            #name::#variant_name(el) => UserControl::draw(el.into(), canvas, parent, state),
52                        },quote! {
53                            impl #impl_generics From<#field_type> for #name #type_generics #where_clause {
54                                fn from(value: #field_type) -> Self {
55                                    #name::#variant_name(value)
56                                }
57                            }
58                        })
59                    }
60                    _ => panic!("Wrong enum format"),
61                }
62            }).collect::<Vec<(proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream)>>();
63            let surfaces = variants.iter().map(|(s, _, _, _, _)| s);
64            let events = variants.iter().map(|(_, s, _, _, _)| s);
65            let updates = variants.iter().map(|(_, _, s, _, _)| s);
66            let draws = variants.iter().map(|(_, _, _, s, _)| s);
67            let froms = variants.iter().map(|(_, _, _, _, s)| s);
68
69            quote! {
70                impl #impl_generics UserControl<#parent, #state> for #name #type_generics #where_clause {
71                    fn surface(this: Ref<Self>, parent: Ref<#parent>, state: Ref<#state>) -> FRect {
72                        match this.as_ref() {
73                            #(#surfaces)*
74                        }
75                    }
76
77                    fn event(
78                        mut this: MutRef<Self>,
79                        canvas: &Canvas<Window>,
80                        event: Event,
81                        parent: MutRef<#parent>,
82                        state: MutRef<#state>,
83                    ) -> Result<()> {
84                        match this.as_mut() {
85                            #(#events)*
86                        }
87                    }
88
89                    fn update(
90                        mut this: MutRef<Self>,
91                        canvas: &Canvas<Window>,
92                        elapsed: Duration,
93                        parent: MutRef<#parent>,
94                        state: MutRef<#state>,
95                    ) -> Result<()> {
96                        match this.as_mut() {
97                            #(#updates)*
98                        }
99                    }
100
101                    fn draw(this: Ref<Self>, canvas: &mut Canvas<Window>, parent: Ref<#parent>, state: Ref<#state>) -> Result<()> {
102                        match this.as_ref() {
103                            #(#draws)*
104                        }
105                    }
106                }
107
108                #(#froms)*
109            }
110        }
111        Data::Struct(data_struct) => {
112            for field in &data_struct.fields {
113                for attr in &field.attrs {
114                    if attr.path().is_ident("child") || attr.path().is_ident("childSelf") {
115                        if child_field.is_some()
116                            || (attr.path().is_ident("child") && attr.path().is_ident("childSelf"))
117                        {
118                            panic!("the struct can't have multiple child.");
119                        }
120                        child_field = Some((
121                            field.ident.clone().expect("Expected named field"),
122                            attr.path().is_ident("childSelf"),
123                        ));
124                    }
125                }
126            }
127            let (child_field, child_self) =
128                child_field.expect("Expected a field with the 'child' attribute");
129
130            let (name_parent, used_parent) = if child_self {
131                (
132                    quote! {_}.to_token_stream(),
133                    quote! {this}.to_token_stream(),
134                )
135            } else {
136                (
137                    quote! {parent}.to_token_stream(),
138                    quote! {parent}.to_token_stream(),
139                )
140            };
141            quote! {
142                impl #impl_generics UserControl<#parent, #state> for #name #type_generics #where_clause {
143                    fn surface(this: Ref<Self>, #name_parent: Ref<#parent>, state: Ref<#state>) -> FRect {
144                        UserControl::surface((&this.#child_field).into(), #used_parent, state)
145                    }
146
147                    fn event( mut this: MutRef<Self>, canvas: &Canvas<Window>, event: Event, #name_parent: MutRef<#parent>, state: MutRef<#state>, ) -> Result<()> {
148                        UserControl::event((&mut this.#child_field).into(), canvas, event, #used_parent, state)
149                    }
150
151                    fn update( mut this: MutRef<Self>, canvas: &Canvas<Window>, elapsed: Duration, #name_parent: MutRef<#parent>, state: MutRef<#state>, ) -> Result<()> {
152                        UserControl::update((&mut this.#child_field).into(), canvas, elapsed, #used_parent, state)
153                    }
154
155                    fn draw(this: Ref<Self>, canvas: &mut Canvas<Window>, #name_parent: Ref<#parent>, state: Ref<#state>) -> Result<()> {
156                        UserControl::draw((&this.#child_field).into(), canvas, #used_parent, state)
157                    }
158                }
159            }
160        }
161        _ => panic!(
162            "\nHow to use the derive macro:\n\n\
163            #[parent(PARENT)]\n\
164            #[state(STATE)]\n\
165            enum A{{\n\
166                Child1(UserControl),\n\
167                Child2(UserControl),\n\
168                ...\n\
169            }}\n\n\
170            #[parent(PARENT)]\n\
171            #[state(STATE)]\n\
172            struct A{{\n\
173                #[child] or #[childSelf] for self as parent\n\
174                Child: UserControl\n\
175            }}"
176        ),
177    };
178    TokenStream::from(expanded)
179}