summaryrefslogtreecommitdiff
path: root/mingling_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src')
-rw-r--r--mingling_macros/src/chain.rs10
-rw-r--r--mingling_macros/src/lib.rs35
-rw-r--r--mingling_macros/src/pack.rs4
-rw-r--r--mingling_macros/src/program_setup.rs199
-rw-r--r--mingling_macros/src/renderer.rs21
5 files changed, 262 insertions, 7 deletions
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs
index f8b1e1c..7b519a1 100644
--- a/mingling_macros/src/chain.rs
+++ b/mingling_macros/src/chain.rs
@@ -93,11 +93,11 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(),
};
- // Ensure the return type is named "GroupProcess"
- if return_type.path.segments.last().unwrap().ident != "GroupProcess" {
+ // Ensure the return type is named "NextProcess"
+ if return_type.path.segments.last().unwrap().ident != "NextProcess" {
return syn::Error::new(
return_type.span(),
- "Return type must be 'mingling::marker::GroupProcess'",
+ "Return type must be 'mingling::marker::NextProcess'",
)
.to_compile_error()
.into();
@@ -134,7 +134,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
async fn proc(#prev_param: Self::Previous) ->
::mingling::ChainProcess<DefaultProgram>
{
- let _ = GroupProcess;
+ let _ = NextProcess;
// Call the original function
#fn_name(#prev_param).await
}
@@ -159,7 +159,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
async fn proc(#prev_param: Self::Previous) ->
::mingling::ChainProcess<#group_name>
{
- let _ = GroupProcess;
+ let _ = NextProcess;
// Call the original function
#fn_name(#prev_param).await
}
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 5a32075..9513875 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -12,6 +12,7 @@ mod chain;
mod dispatcher_chain;
mod node;
mod pack;
+mod program_setup;
mod render;
mod renderer;
@@ -19,6 +20,9 @@ use once_cell::sync::Lazy;
use std::sync::Mutex;
// Global variable declarations for storing chain and renderer mappings
+#[cfg(feature = "general_renderer")]
+pub(crate) static GENERAL_RENDERERS: Lazy<Mutex<Vec<String>>> =
+ Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static PACKED_TYPES: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static CHAINS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub(crate) static RENDERERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
@@ -65,6 +69,11 @@ pub fn renderer(_attr: TokenStream, item: TokenStream) -> TokenStream {
renderer::renderer_attr(item)
}
+#[proc_macro_attribute]
+pub fn program_setup(attr: TokenStream, item: TokenStream) -> TokenStream {
+ program_setup::setup_attr(attr, item)
+}
+
#[proc_macro]
pub fn gen_program(input: TokenStream) -> TokenStream {
let name = if input.is_empty() {
@@ -83,6 +92,9 @@ pub fn gen_program(input: TokenStream) -> TokenStream {
let renderer_exist = RENDERERS_EXIST.lock().unwrap().clone();
let chain_exist = CHAINS_EXIST.lock().unwrap().clone();
+ #[cfg(feature = "general_renderer")]
+ let general_renderers = GENERAL_RENDERERS.lock().unwrap().clone();
+
let packed_types: Vec<proc_macro2::TokenStream> = packed_types
.iter()
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
@@ -108,6 +120,28 @@ pub fn gen_program(input: TokenStream) -> TokenStream {
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
.collect();
+ #[cfg(feature = "general_renderer")]
+ let general_renderer_tokens: Vec<proc_macro2::TokenStream> = general_renderers
+ .iter()
+ .map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
+ .collect();
+
+ #[cfg(feature = "general_renderer")]
+ let general_render = quote! {
+ fn general_render(
+ any: ::mingling::AnyOutput<Self::Enum>,
+ setting: &::mingling::GeneralRendererSetting,
+ ) -> Result<::mingling::RenderResult, ::mingling::error::GeneralRendererSerializeError> {
+ match any.member_id {
+ #(#general_renderer_tokens)*
+ _ => Ok(::mingling::RenderResult::default()),
+ }
+ }
+ };
+
+ #[cfg(not(feature = "general_renderer"))]
+ let general_render = quote! {};
+
let expanded = quote! {
::mingling::macros::pack!(#name, RendererNotFound = String);
::mingling::macros::pack!(#name, DispatcherNotFound = Vec<String>);
@@ -155,6 +189,7 @@ pub fn gen_program(input: TokenStream) -> TokenStream {
_ => false
}
}
+ #general_render
}
impl #name {
diff --git a/mingling_macros/src/pack.rs b/mingling_macros/src/pack.rs
index c6a6c67..a84010e 100644
--- a/mingling_macros/src/pack.rs
+++ b/mingling_macros/src/pack.rs
@@ -77,7 +77,7 @@ pub fn pack(input: TokenStream) -> TokenStream {
};
// Generate the struct definition
- #[cfg(not(feature = "serde"))]
+ #[cfg(not(feature = "general_renderer"))]
let struct_def = quote! {
#[derive(Debug)]
pub struct #type_name {
@@ -85,7 +85,7 @@ pub fn pack(input: TokenStream) -> TokenStream {
}
};
- #[cfg(feature = "serde")]
+ #[cfg(feature = "general_renderer")]
let struct_def = quote! {
#[derive(Debug, serde::Serialize)]
pub struct #type_name {
diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs
new file mode 100644
index 0000000..54da898
--- /dev/null
+++ b/mingling_macros/src/program_setup.rs
@@ -0,0 +1,199 @@
+//! Setup Attribute Macro Implementation
+//!
+//! This module provides the `#[setup]` attribute macro for automatically
+//! generating structs that implement the `ProgramSetup` trait from functions.
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::spanned::Spanned;
+use syn::{
+ FnArg, Ident, ItemFn, Pat, PatType, ReturnType, Signature, Type, TypePath, parse_macro_input,
+};
+
+/// Extracts the program parameter from function arguments
+fn extract_program_param(sig: &Signature) -> syn::Result<(Pat, TypePath)> {
+ // The function should have exactly one parameter
+ if sig.inputs.len() != 1 {
+ return Err(syn::Error::new(
+ sig.inputs.span(),
+ "Setup function must have exactly one parameter",
+ ));
+ }
+
+ let arg = &sig.inputs[0];
+ match arg {
+ FnArg::Typed(PatType { pat, ty, .. }) => {
+ // Extract the pattern (parameter name)
+ let param_pat = (**pat).clone();
+
+ // Extract the type, handling references like &mut ThisProgram
+ match &**ty {
+ Type::Path(type_path) => Ok((param_pat, type_path.clone())),
+ Type::Reference(type_ref) => {
+ // Handle reference types like &mut ThisProgram
+ match &*type_ref.elem {
+ Type::Path(type_path) => Ok((param_pat, type_path.clone())),
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Reference parameter must point to a type path",
+ )),
+ }
+ }
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Parameter type must be a type path or reference to a type path",
+ )),
+ }
+ }
+ FnArg::Receiver(_) => Err(syn::Error::new(
+ arg.span(),
+ "Setup function cannot have self parameter",
+ )),
+ }
+}
+
+/// Validates that the parameter type is `ThisProgram`
+fn validate_any_program_param(type_path: &TypePath) -> syn::Result<()> {
+ // Check if the type is `ThisProgram`
+ let segments = &type_path.path.segments;
+ if segments.len() == 1 && segments[0].ident == "ThisProgram" {
+ Ok(())
+ } else {
+ // Check if it's a qualified path like mingling::marker::ThisProgram
+ let mut is_any_program = false;
+ if segments.len() > 1 {
+ // Check if the last segment is "ThisProgram"
+ if segments.last().unwrap().ident == "ThisProgram" {
+ is_any_program = true;
+ }
+ }
+
+ if is_any_program {
+ Ok(())
+ } else {
+ Err(syn::Error::new(
+ type_path.span(),
+ "Setup function parameter must be `mingling::marker::ThisProgram`",
+ ))
+ }
+ }
+}
+
+/// Extracts and validates the return type
+fn extract_return_type(sig: &Signature) -> syn::Result<()> {
+ // Setup functions should return () or have no return type
+ match &sig.output {
+ ReturnType::Type(_, ty) => {
+ // Check if it's ()
+ match &**ty {
+ Type::Tuple(tuple) if tuple.elems.is_empty() => Ok(()),
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Setup function must return () or have no return type",
+ )),
+ }
+ }
+ ReturnType::Default => Ok(()),
+ }
+}
+
+pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
+ // Parse the attribute arguments (e.g., MyProgram from #[setup(MyProgram)])
+ // If no argument is provided, use DefaultProgram
+ let (program_name, use_crate_prefix) = if attr.is_empty() {
+ (
+ Ident::new("DefaultProgram", proc_macro2::Span::call_site()),
+ true,
+ )
+ } else {
+ (parse_macro_input!(attr as Ident), false)
+ };
+
+ // Parse the function item
+ let input_fn = parse_macro_input!(item as ItemFn);
+
+ // Validate the function is not async
+ if input_fn.sig.asyncness.is_some() {
+ return syn::Error::new(input_fn.sig.span(), "Setup function cannot be async")
+ .to_compile_error()
+ .into();
+ }
+
+ // Extract the program parameter
+ let (program_param, program_type) = match extract_program_param(&input_fn.sig) {
+ Ok(info) => info,
+ Err(e) => return e.to_compile_error().into(),
+ };
+
+ // Validate that the parameter is ThisProgram
+ if let Err(e) = validate_any_program_param(&program_type) {
+ return e.to_compile_error().into();
+ }
+
+ // Validate return type
+ if let Err(e) = extract_return_type(&input_fn.sig) {
+ return e.to_compile_error().into();
+ }
+
+ // Get the function body
+ let fn_body = &input_fn.block;
+
+ // Get function attributes (excluding the setup attribute)
+ let mut fn_attrs = input_fn.attrs.clone();
+
+ // Remove any #[setup(...)] attributes to avoid infinite recursion
+ fn_attrs.retain(|attr| !attr.path().is_ident("setup"));
+
+ // Get function visibility
+ let vis = &input_fn.vis;
+
+ // Get function name
+ let fn_name = &input_fn.sig.ident;
+
+ // Generate struct name from function name using pascal_case
+ let pascal_case_name = just_fmt::pascal_case!(fn_name.to_string());
+ let struct_name = Ident::new(&pascal_case_name, fn_name.span());
+
+ // Generate the struct and implementation
+ let expanded = if use_crate_prefix {
+ quote! {
+ #(#fn_attrs)*
+ #vis struct #struct_name;
+
+ impl ::mingling::setup::ProgramSetup<DefaultProgram, DefaultProgram> for #struct_name {
+ fn setup(&mut self, program: &mut ::mingling::Program<DefaultProgram, DefaultProgram>) {
+ let _ = ThisProgram;
+ // Call the original function with the actual Program type
+ #fn_name(program);
+ }
+ }
+
+ // Keep the original function for internal use
+ #(#fn_attrs)*
+ #vis fn #fn_name(#program_param: &mut ::mingling::Program<DefaultProgram, DefaultProgram>) {
+ #fn_body
+ }
+ }
+ } else {
+ quote! {
+ #(#fn_attrs)*
+ #vis struct #struct_name;
+
+ impl ::mingling::setup::ProgramSetup<#program_name, #program_name> for #struct_name {
+ fn setup(&mut self, program: &mut ::mingling::Program<#program_name, #program_name>) {
+ let _ = ThisProgram;
+ // Call the original function with the actual Program type
+ #fn_name(program);
+ }
+ }
+
+ // Keep the original function for internal use
+ #(#fn_attrs)*
+ #vis fn #fn_name(#program_param: &mut ::mingling::Program<#program_name, #program_name>) {
+ #fn_body
+ }
+ }
+ };
+
+ expanded.into()
+}
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index 0e32b40..4edac88 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -109,14 +109,30 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream {
Self::#previous_type => true,
};
+ #[cfg(feature = "general_renderer")]
+ let general_renderer_entry = quote! {
+ Self::#previous_type => {
+ let raw = any.restore::<#previous_type>().unwrap();
+ let mut r = ::mingling::RenderResult::default();
+ ::mingling::GeneralRenderer::render(&raw, setting, &mut r)?;
+ Ok(r)
+ }
+ };
+
let mut renderers = crate::RENDERERS.lock().unwrap();
let mut renderer_exist = crate::RENDERERS_EXIST.lock().unwrap();
let mut packed_types = crate::PACKED_TYPES.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();
let previous_type_str = previous_type.to_token_stream().to_string();
+ #[cfg(feature = "general_renderer")]
+ let general_renderer_entry_str = general_renderer_entry.to_string();
+
if !renderers.contains(&renderer_entry_str) {
renderers.push(renderer_entry_str);
}
@@ -129,6 +145,11 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream {
packed_types.push(previous_type_str);
}
+ #[cfg(feature = "general_renderer")]
+ if !general_renderers.contains(&general_renderer_entry_str) {
+ general_renderers.push(general_renderer_entry_str);
+ }
+
// Generate the struct and implementation
// We need to create a wrapper function that adds the r parameter
let expanded = quote! {