diff options
| -rw-r--r-- | mingling/src/lib.rs | 4 | ||||
| -rw-r--r-- | mingling_macros/src/chain.rs | 69 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 13 | ||||
| -rw-r--r-- | mingling_macros/src/renderer.rs | 87 |
4 files changed, 129 insertions, 44 deletions
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 12d4a84..858b4b7 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -98,6 +98,10 @@ pub mod macros { pub use mingling_macros::r_print; /// Used to print content with a newline within a `Renderer` context pub use mingling_macros::r_println; + /// Used to register a chain + pub use mingling_macros::register_chain; + /// Used to register a renderer + pub use mingling_macros::register_renderer; /// Used to register a type into the context pub use mingling_macros::register_type; /// Used to generate a struct implementing the `Renderer` trait via a method diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs index 84353e9..14e62ec 100644 --- a/mingling_macros/src/chain.rs +++ b/mingling_macros/src/chain.rs @@ -4,7 +4,7 @@ //! generating structs that implement the `Chain` trait from async functions. use proc_macro::TokenStream; -use quote::quote; +use quote::{ToTokens, quote}; use syn::spanned::Spanned; use syn::{ FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input, @@ -129,7 +129,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { #[doc(hidden)] #vis struct #struct_name; - ::mingling::macros::register_type!(#previous_type); + ::mingling::macros::register_chain!(#previous_type, #struct_name); impl ::mingling::Chain<ThisProgram> for #struct_name { type Previous = #previous_type; @@ -156,6 +156,8 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { #(#fn_attrs)* #vis struct #struct_name; + ::mingling::macros::register_chain!(#previous_type, #struct_name); + impl ::mingling::Chain<#group_name> for #struct_name { type Previous = #previous_type; @@ -178,19 +180,6 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream { } }; - // Record the chain mapping - let chain_entry = build_chain_arm(&struct_name, &previous_type); - let chain_exist_entry = build_chain_exist_arm(&previous_type); - - let mut chains = crate::CHAINS.lock().unwrap(); - let mut chain_exist = crate::CHAINS_EXIST.lock().unwrap(); - - let chain_entry = chain_entry.to_string(); - let chain_exist_entry = chain_exist_entry.to_string(); - - chains.insert(chain_entry); - chain_exist.insert(chain_exist_entry); - expanded.into() } @@ -207,3 +196,53 @@ pub fn build_chain_exist_arm(previous_type: &TypePath) -> proc_macro2::TokenStre Self::#previous_type => true, } } + +pub fn register_chain(input: TokenStream) -> TokenStream { + // Parse the input as a comma-separated list of arguments + let input_parsed = syn::parse_macro_input!(input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated); + + // Check that we have exactly two elements + if input_parsed.len() != 2 { + return syn::Error::new( + input_parsed.span(), + "Expected exactly two comma-separated arguments: `PreviousType, StructName`", + ) + .to_compile_error() + .into(); + } + + // Extract the two elements + let previous_type_expr = &input_parsed[0]; + let struct_name_expr = &input_parsed[1]; + + // Convert expressions to TypePath and Ident + let previous_type = match syn::parse2::<TypePath>(previous_type_expr.to_token_stream()) { + Ok(ty) => ty, + Err(e) => return e.to_compile_error().into(), + }; + + let struct_name = match syn::parse2::<syn::Ident>(struct_name_expr.to_token_stream()) { + Ok(ident) => ident, + Err(e) => return e.to_compile_error().into(), + }; + + // Record the chain mapping: previous_type => struct_name + let chain_entry = build_chain_arm(&struct_name, &previous_type); + + // Record the chain existence check + let chain_exist_entry = build_chain_exist_arm(&previous_type); + + let mut chains = crate::CHAINS.lock().unwrap(); + let mut chain_exist = crate::CHAINS_EXIST.lock().unwrap(); + + let chain_entry_str = chain_entry.to_string(); + let chain_exist_entry_str = chain_exist_entry.to_string(); + + chains.insert(chain_entry_str); + chain_exist.insert(chain_exist_entry_str); + + quote! { + ::mingling::macros::register_type!(#previous_type); + } + .into() +} diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 8ddda8b..5391dfd 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -169,9 +169,6 @@ pub fn program_gen_completion(input: TokenStream) -> TokenStream { TokenStream::from(comp_dispatcher) } -/// Internal macro for registering types. -/// -/// This macro is used internally by the `#[chain]` and `#[renderer]` attribute macros #[proc_macro] pub fn register_type(input: TokenStream) -> TokenStream { let type_ident = parse_macro_input!(input as syn::Ident); @@ -183,6 +180,16 @@ pub fn register_type(input: TokenStream) -> TokenStream { } #[proc_macro] +pub fn register_chain(input: TokenStream) -> TokenStream { + chain::register_chain(input) +} + +#[proc_macro] +pub fn register_renderer(input: TokenStream) -> TokenStream { + renderer::register_renderer(input) +} + +#[proc_macro] pub fn program_final_gen(input: TokenStream) -> TokenStream { let name = read_name(&input); diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs index 0f477ee..a3da2e7 100644 --- a/mingling_macros/src/renderer.rs +++ b/mingling_macros/src/renderer.rs @@ -4,7 +4,7 @@ //! generating structs that implement the `Renderer` trait from functions. use proc_macro::TokenStream; -use quote::quote; +use quote::{ToTokens, quote}; use syn::spanned::Spanned; use syn::{FnArg, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input}; @@ -100,30 +100,6 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream { let pascal_case_name = just_fmt::pascal_case!(fn_name.to_string()); let struct_name = syn::Ident::new(&pascal_case_name, fn_name.span()); - // Register the renderer in the global list - let renderer_entry = build_renderer_entry(&struct_name, &previous_type); - let renderer_exist_entry = build_renderer_exist_entry(&previous_type); - #[cfg(feature = "general_renderer")] - let general_renderer_entry = build_general_renderer_entry(&previous_type); - - let mut renderers = crate::RENDERERS.lock().unwrap(); - let mut renderer_exist = crate::RENDERERS_EXIST.lock().unwrap(); - - #[cfg(feature = "general_renderer")] - let mut general_renderers = crate::GENERAL_RENDERERS.lock().unwrap(); - - let renderer_entry_str = renderer_entry.to_string(); - let renderer_exist_entry_str = renderer_exist_entry.to_string(); - - #[cfg(feature = "general_renderer")] - let general_renderer_entry_str = general_renderer_entry.to_string(); - - renderers.insert(renderer_entry_str); - renderer_exist.insert(renderer_exist_entry_str); - - #[cfg(feature = "general_renderer")] - general_renderers.insert(general_renderer_entry_str); - // Generate the struct and implementation // We need to create a wrapper function that adds the r parameter let expanded = quote! { @@ -131,7 +107,7 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream { #[doc(hidden)] #vis struct #struct_name; - ::mingling::macros::register_type!(#previous_type); + ::mingling::macros::register_renderer!(#previous_type, #struct_name); impl ::mingling::Renderer for #struct_name { type Previous = #previous_type; @@ -192,3 +168,62 @@ pub fn build_general_renderer_entry(previous_type: &TypePath) -> proc_macro2::To } } } + +pub fn register_renderer(input: TokenStream) -> TokenStream { + // Parse the input as a comma-separated list of arguments + let input_parsed = syn::parse_macro_input!(input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated); + + // Check that we have exactly two elements + if input_parsed.len() != 2 { + return syn::Error::new( + input_parsed.span(), + "Expected exactly two comma-separated arguments: `PreviousType, StructName`", + ) + .to_compile_error() + .into(); + } + + // Extract the two elements + let previous_type_expr = &input_parsed[0]; + let struct_name_expr = &input_parsed[1]; + + // Convert expressions to TypePath and Ident + let previous_type = match syn::parse2::<TypePath>(previous_type_expr.to_token_stream()) { + Ok(ty) => ty, + Err(e) => return e.to_compile_error().into(), + }; + + let struct_name = match syn::parse2::<syn::Ident>(struct_name_expr.to_token_stream()) { + Ok(ident) => ident, + Err(e) => return e.to_compile_error().into(), + }; + + // Register the renderer in the global list + let renderer_entry = build_renderer_entry(&struct_name, &previous_type); + let renderer_exist_entry = build_renderer_exist_entry(&previous_type); + #[cfg(feature = "general_renderer")] + let general_renderer_entry = build_general_renderer_entry(&previous_type); + + let mut renderers = crate::RENDERERS.lock().unwrap(); + let mut renderer_exist = crate::RENDERERS_EXIST.lock().unwrap(); + + #[cfg(feature = "general_renderer")] + let mut general_renderers = crate::GENERAL_RENDERERS.lock().unwrap(); + + let renderer_entry_str = renderer_entry.to_string(); + let renderer_exist_entry_str = renderer_exist_entry.to_string(); + + #[cfg(feature = "general_renderer")] + let general_renderer_entry_str = general_renderer_entry.to_string(); + + renderers.insert(renderer_entry_str); + renderer_exist.insert(renderer_exist_entry_str); + + #[cfg(feature = "general_renderer")] + general_renderers.insert(general_renderer_entry_str); + + quote! { + ::mingling::macros::register_type!(#previous_type); + } + .into() +} |
