jsi-macros 0.3.1-alpha.5

Support macros for jsi-rs
Documentation
use inflector::Inflector;
use proc_macro2::TokenStream;

use quote::{format_ident, quote};
use syn::{parse::Parse, ItemEnum};

extern crate proc_macro;
pub struct HostEventImpl(pub TokenStream);

impl Parse for HostEventImpl {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let enum_def: ItemEnum = input.parse()?;

        let enum_name = &enum_def.ident;

        let event_key_name = format_ident!("{}Key", enum_name);

        let mut event_names = vec![];
        let mut event_key_mappers = vec![];
        let mut event_args_mappers = vec![];

        for variant in &enum_def.variants {
            let variant_name = variant.ident.clone();
            event_names.push(variant_name.clone());

            event_key_mappers.push(match &variant.fields {
                syn::Fields::Named(_) => {
                    quote! { Self::#variant_name { .. } => #event_key_name::#variant_name }
                }
                syn::Fields::Unnamed(_) => {
                    quote! { Self::#variant_name ( .. ) => #event_key_name::#variant_name }
                }
                syn::Fields::Unit => {
                    quote! { Self::#variant_name => #event_key_name::#variant_name }
                }
            });

            event_args_mappers.push(match &variant.fields {
                syn::Fields::Named(fields) => {
                    let field_names: Vec<_> = fields
                        .named
                        .iter()
                        .map(|f| f.ident.clone().unwrap())
                        .collect();

                    quote! { Self::#variant_name { #(#field_names),* } => {
                        let mut args: Vec<::jsi::JsiValue> = vec![];
                        #( args.push(::jsi::IntoValue::into_value(#field_names, rt)); )*
                        args
                    } }
                }
                syn::Fields::Unnamed(fields) => {
                    let field_names: Vec<_> = fields
                        .unnamed
                        .iter()
                        .enumerate()
                        .map(|(idx, _)| format_ident!("f{}", idx))
                        .collect();

                    quote! { Self::#variant_name { #(#field_names),* } => {
                        let mut args: Vec<::jsi::JsiValue> = vec![];
                        #( args.push(::jsi::IntoValue::into_value(#field_names, rt)); )*
                        args
                    } }
                }
                syn::Fields::Unit => {
                    quote! { Self::#variant_name => vec![], }
                }
            });
        }

        let event_key_def = quote! {
            #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
            #[automatically_derived]
            pub enum #event_key_name {
                #(#event_names),*
            }
        };

        let event_names_str = event_names.iter().map(|id| id.to_string().to_camel_case());

        let event_key_impl_def = quote! {
            #[automatically_derived]
            impl ::std::str::FromStr for #event_key_name {
                type Err = ::anyhow::Error;

                fn from_str(s: &str) -> Result<Self, Self::Err> {
                    match s {
                        #(#event_names_str => Ok(Self::#event_names),)*
                        _ => bail!("invalid event name")
                    }
                }
            }

            #[automatically_derived]
            impl ::splicer_js_api::event::HostEventKey for #event_key_name {
            }
        };

        let has_rt_lifetime = enum_def.generics.params.iter().any(|p| match p {
            syn::GenericParam::Lifetime(lt) => lt.lifetime.ident == "rt",
            _ => false,
        });

        let enum_impl_generics = if has_rt_lifetime {
            quote! { <'rt> }
        } else {
            quote! {}
        };

        let event_impl_def = quote! {
            #[automatically_derived]
            impl<'rt> ::splicer_js_api::event::HostEvent<'rt> for #enum_name #enum_impl_generics {
                type Key = #event_key_name;

                fn key(&self) -> Self::Key {
                    match self {
                        #(#event_key_mappers),*
                    }
                }

                fn args(self, rt: &mut ::jsi::RuntimeHandle<'rt>) -> Vec<JsiValue<'rt>> {
                    match self {
                        #(#event_args_mappers),*
                    }
                }
            }
        };

        Ok(Self(quote! {
            #event_key_def

            #event_key_impl_def

            #event_impl_def
        }))
    }
}