natrix_macros 0.1.0

Macros for natrix
Documentation
extern crate proc_macro;
use proc_macro2::TokenStream;
use quote::format_ident;
use syn::ItemStruct;
use template_quote::{ToTokens, quote};

#[proc_macro_derive(Component)]
pub fn component_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let item = syn::parse_macro_input!(item as ItemStruct);
    let result = implementation(item);
    result.into()
}

fn implementation(item: ItemStruct) -> TokenStream {
    let name = item.ident.clone();
    let (fields, is_named) = get_fields(item.fields);

    let data_name = format_ident!("_{name}Data");

    quote! {
        #[doc(hidden)]
        #(if is_named) {
            pub struct #data_name {
                #(for field in &fields) {
                        pub #{field.access.clone()}: ::natrix::macro_ref::Signal<#{field.type_.clone()}, Self>,
                }
            }
        } #(else) {
            pub struct #data_name(
                #(for field in &fields) {
                        pub ::natrix::macro_ref::Signal<#{field.type_.clone()}, Self>,
                }
            );
        }

        impl ::natrix::macro_ref::ComponentData for #data_name {
            fn signals(&self) -> ::std::vec::Vec<&dyn ::natrix::macro_ref::SignalMethods<Self>> {
                ::std::vec![
                    #(for field in &fields) {
                        &self.#{field.access.clone()},
                    }
                ]
            }
            fn signals_mut(&mut self) -> ::std::vec::Vec<&mut dyn ::natrix::macro_ref::SignalMethods<Self>> {
                ::std::vec![
                    #(for field in &fields) {
                        &mut self.#{field.access.clone()},
                    }
                ]
            }
        }

        impl ::natrix::macro_ref::ComponentBase for #name {
            type Data = #data_name;
            fn into_data(self) -> Self::Data {
                #data_name {
                    #(for field in fields) {
                        #{field.access.clone()}: ::natrix::macro_ref::Signal::new(self.#{field.access}),
                    }
                }
            }
        }
    }
}

fn get_fields(fields: syn::Fields) -> (Vec<Field>, bool) {
    match fields {
        syn::Fields::Unit => (vec![], true),
        syn::Fields::Named(fields) => (
            fields
                .named
                .into_iter()
                .map(|field| Field {
                    type_: field.ty.into_token_stream(),
                    access: field.ident.into_token_stream(),
                })
                .collect(),
            true,
        ),
        syn::Fields::Unnamed(fields) => (
            fields
                .unnamed
                .into_iter()
                .enumerate()
                .map(|(index, field)| Field {
                    type_: field.ty.to_token_stream(),
                    access: proc_macro2::Literal::usize_unsuffixed(index).to_token_stream(),
                })
                .collect(),
            false,
        ),
    }
}

struct Field {
    type_: TokenStream,
    access: TokenStream,
}