red-sdl-macro 0.2.0

sdl with grid layout and window management to help build apps or video game.
Documentation
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields};

#[allow(clippy::too_many_lines)]
#[proc_macro_derive(UserControl, attributes(parent, state, child, childSelf))]
pub fn user_control_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();

    let mut parent = parse_quote!(());
    let mut state = parse_quote!(());
    let mut child_field = None;

    for attr in &input.attrs {
        if attr.path().is_ident("parent") {
            attr.parse_nested_meta(|meta| {
                parent = meta.path.to_token_stream();
                Ok(())
            })
            .expect("Error parsing parent type attribute");
        } else if attr.path().is_ident("state") {
            attr.parse_nested_meta(|meta| {
                state = meta.path.to_token_stream();
                Ok(())
            })
            .expect("Error parsing state type attribute");
        }
    }

    let expanded = match &input.data {
        Data::Enum(data_enum) => {
            let variants = data_enum.variants.iter().map(|variant| {
                let variant_name = &variant.ident;
                match &variant.fields {
                    Fields::Unnamed(fields) => {
                        if fields.unnamed.len() != 1{
                            panic!("Multiple element in enum case");
                        }
                        let field_type = fields.unnamed.first().expect("").ty.to_token_stream();
                        (quote! {
                            #name::#variant_name(el) => UserControl::surface(el.into(), parent, state),
                        },quote! {
                            #name::#variant_name(el) => UserControl::event(el.into(), canvas, event, parent, state),
                        },quote! {
                            #name::#variant_name(el) => UserControl::update(el.into(), canvas, elapsed, parent, state),
                        },quote! {
                            #name::#variant_name(el) => UserControl::draw(el.into(), canvas, parent, state),
                        },quote! {
                            impl #impl_generics From<#field_type> for #name #type_generics #where_clause {
                                fn from(value: #field_type) -> Self {
                                    #name::#variant_name(value)
                                }
                            }
                        })
                    }
                    _ => panic!("Wrong enum format"),
                }
            }).collect::<Vec<(proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream,proc_macro2::TokenStream)>>();
            let surfaces = variants.iter().map(|(s, _, _, _, _)| s);
            let events = variants.iter().map(|(_, s, _, _, _)| s);
            let updates = variants.iter().map(|(_, _, s, _, _)| s);
            let draws = variants.iter().map(|(_, _, _, s, _)| s);
            let froms = variants.iter().map(|(_, _, _, _, s)| s);

            quote! {
                impl #impl_generics UserControl<#parent, #state> for #name #type_generics #where_clause {
                    fn surface(this: Ref<Self>, parent: Ref<#parent>, state: Ref<#state>) -> FRect {
                        match this.as_ref() {
                            #(#surfaces)*
                        }
                    }

                    fn event(
                        mut this: MutRef<Self>,
                        canvas: &Canvas<Window>,
                        event: Event,
                        parent: MutRef<#parent>,
                        state: MutRef<#state>,
                    ) -> Result<()> {
                        match this.as_mut() {
                            #(#events)*
                        }
                    }

                    fn update(
                        mut this: MutRef<Self>,
                        canvas: &Canvas<Window>,
                        elapsed: Duration,
                        parent: MutRef<#parent>,
                        state: MutRef<#state>,
                    ) -> Result<()> {
                        match this.as_mut() {
                            #(#updates)*
                        }
                    }

                    fn draw(this: Ref<Self>, canvas: &mut Canvas<Window>, parent: Ref<#parent>, state: Ref<#state>) -> Result<()> {
                        match this.as_ref() {
                            #(#draws)*
                        }
                    }
                }

                #(#froms)*
            }
        }
        Data::Struct(data_struct) => {
            for field in &data_struct.fields {
                for attr in &field.attrs {
                    if attr.path().is_ident("child") || attr.path().is_ident("childSelf") {
                        if child_field.is_some()
                            || (attr.path().is_ident("child") && attr.path().is_ident("childSelf"))
                        {
                            panic!("the struct can't have multiple child.");
                        }
                        child_field = Some((
                            field.ident.clone().expect("Expected named field"),
                            attr.path().is_ident("childSelf"),
                        ));
                    }
                }
            }
            let (child_field, child_self) =
                child_field.expect("Expected a field with the 'child' attribute");

            let (name_parent, used_parent) = if child_self {
                (
                    quote! {_}.to_token_stream(),
                    quote! {this}.to_token_stream(),
                )
            } else {
                (
                    quote! {parent}.to_token_stream(),
                    quote! {parent}.to_token_stream(),
                )
            };
            quote! {
                impl #impl_generics UserControl<#parent, #state> for #name #type_generics #where_clause {
                    fn surface(this: Ref<Self>, #name_parent: Ref<#parent>, state: Ref<#state>) -> FRect {
                        UserControl::surface((&this.#child_field).into(), #used_parent, state)
                    }

                    fn event( mut this: MutRef<Self>, canvas: &Canvas<Window>, event: Event, #name_parent: MutRef<#parent>, state: MutRef<#state>, ) -> Result<()> {
                        UserControl::event((&mut this.#child_field).into(), canvas, event, #used_parent, state)
                    }

                    fn update( mut this: MutRef<Self>, canvas: &Canvas<Window>, elapsed: Duration, #name_parent: MutRef<#parent>, state: MutRef<#state>, ) -> Result<()> {
                        UserControl::update((&mut this.#child_field).into(), canvas, elapsed, #used_parent, state)
                    }

                    fn draw(this: Ref<Self>, canvas: &mut Canvas<Window>, #name_parent: Ref<#parent>, state: Ref<#state>) -> Result<()> {
                        UserControl::draw((&this.#child_field).into(), canvas, #used_parent, state)
                    }
                }
            }
        }
        _ => panic!(
            "\nHow to use the derive macro:\n\n\
            #[parent(PARENT)]\n\
            #[state(STATE)]\n\
            enum A{{\n\
                Child1(UserControl),\n\
                Child2(UserControl),\n\
                ...\n\
            }}\n\n\
            #[parent(PARENT)]\n\
            #[state(STATE)]\n\
            struct A{{\n\
                #[child] or #[childSelf] for self as parent\n\
                Child: UserControl\n\
            }}"
        ),
    };
    TokenStream::from(expanded)
}