aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--mingling_core/src/program.rs73
-rw-r--r--mingling_macros/src/lib.rs113
3 files changed, 109 insertions, 79 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9b7bd7..637b074 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,6 +72,8 @@
2. **\[core:comp\]** Changed the completion system's node filtering to exclude all hidden nodes (names starting with `_`) instead of only the specific `__comp` node. This makes the completion script generation more general — any node prefixed with an underscore is now treated as internal/hidden and excluded from suggestions.
+3. **\[macros\]** Consolidated `__dispatch_program_renderers!` and `__dispatch_program_chains!` from `macro_rules!` into the `program_final_gen` proc-macro (`mingling_macros/src/lib.rs`), removing them from `mingling_core/src/program.rs`. The `render()` and `do_chain()` match dispatch is now generated directly by the proc-macro, using a compile-time `ASYNC_ENABLED` constant (via `#[cfg(feature = "async")]`) to select the correct sync/async signature at proc-macro compilation time, replacing the previous `#[cfg]`-gated `macro_rules!` dispatch that relied on per-crate feature resolution.
+
#### Features:
1. **\[core\]** Added the `unpack_chain_process!` macro for ergonomically extracting the inner value from a `ChainProcess` result.
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 12dc9cc..a1b803e 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -169,79 +169,6 @@ where
}
}
-#[macro_export]
-#[doc(hidden)]
-macro_rules! __dispatch_program_renderers {
- (
- $( $render_ty:ty => $prev_ty:ident, )*
- ) => {
- fn render(any: mingling::AnyOutput<Self::Enum>, __renderer_inner_result: &mut mingling::RenderResult) {
- match any.member_id {
- $(
- Self::$prev_ty => {
- // SAFETY: The `type_id` check ensures that `any` contains a value of type `$prev_ty`,
- // so downcasting to `$prev_ty` is safe.
- let value = unsafe { any.downcast::<$prev_ty>().unwrap_unchecked() };
- <$render_ty as mingling::Renderer>::render(value, __renderer_inner_result);
- }
- )*
- _ => (),
- }
- }
- };
-}
-
-#[macro_export]
-#[doc(hidden)]
-#[cfg(feature = "async")]
-macro_rules! __dispatch_program_chains {
- (
- $( $chain_ty:ty => $chain_prev:ident, )*
- ) => {
- fn do_chain(
- any: mingling::AnyOutput<Self::Enum>,
- ) -> std::pin::Pin<Box<dyn Future<Output = mingling::ChainProcess<Self::Enum>> + Send>> {
- match any.member_id {
- $(
- Self::$chain_prev => {
- // SAFETY: The `type_id` check ensures that `any` contains a value of type `$chain_prev`,
- // so downcasting to `$chain_prev` is safe.
- let value = unsafe { any.downcast::<$chain_prev>().unwrap_unchecked() };
- let fut = async { <$chain_ty as mingling::Chain<Self::Enum>>::proc(value).await };
- Box::pin(fut)
- }
- )*
- _ => panic!("No chain found for type id: {:?}", any.type_id),
- }
- }
- };
-}
-
-#[macro_export]
-#[doc(hidden)]
-#[cfg(not(feature = "async"))]
-macro_rules! __dispatch_program_chains {
- (
- $( $chain_ty:ty => $chain_prev:ident, )*
- ) => {
- fn do_chain(
- any: mingling::AnyOutput<Self::Enum>,
- ) -> mingling::ChainProcess<Self::Enum> {
- match any.member_id {
- $(
- Self::$chain_prev => {
- // SAFETY: The `type_id` check ensures that `any` contains a value of type `$chain_prev`,
- // so downcasting to `$chain_prev` is safe.
- let value = unsafe { any.downcast::<$chain_prev>().unwrap_unchecked() };
- <$chain_ty as mingling::Chain<Self::Enum>>::proc(value)
- }
- )*
- _ => panic!("No chain found for type id: {:?}", any.type_id),
- }
- }
- };
-}
-
/// Get all registered dispatcher names from the program
#[allow(unused_variables)]
#[must_use]
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 3b33f09..c6b94c3 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -1624,6 +1624,25 @@ pub fn program_fallback_gen(_input: TokenStream) -> TokenStream {
///
/// # Panics
///
+// Feature detection: baked into the proc-macro binary at compile time
+#[cfg(feature = "async")]
+const ASYNC_ENABLED: bool = true;
+#[cfg(not(feature = "async"))]
+const ASYNC_ENABLED: bool = false;
+
+/// Parses an entry of the format `StructName => EnumVariant,` into a pair of idents.
+fn parse_entry_pair(entry: &proc_macro2::TokenStream) -> (proc_macro2::Ident, proc_macro2::Ident) {
+ let s = entry.to_string();
+ let arrow_idx = s
+ .find("=>")
+ .unwrap_or_else(|| panic!("Entry missing '=>': {s}"));
+ let struct_str = s[..arrow_idx].trim();
+ let variant_str = s[arrow_idx + 2..].trim().trim_end_matches(',');
+ let struct_ident = proc_macro2::Ident::new(struct_str, proc_macro2::Span::call_site());
+ let variant_ident = proc_macro2::Ident::new(variant_str, proc_macro2::Span::call_site());
+ (struct_ident, variant_ident)
+}
+
/// Panics if any of the global registries (`PACKED_TYPES`, `RENDERERS`, `CHAINS`, etc.)
/// are poisoned.
#[proc_macro]
@@ -1749,6 +1768,92 @@ pub fn program_final_gen(_input: TokenStream) -> TokenStream {
#[cfg(not(feature = "comp"))]
let comp = quote! {};
+ // Build render function arms from stored entries
+ let render_fn = if renderer_tokens.is_empty() {
+ quote! {
+ fn render(_any: ::mingling::AnyOutput<Self::Enum>, _renderer_inner_result: &mut ::mingling::RenderResult) {}
+ }
+ } else {
+ let render_arms: Vec<_> = renderer_tokens.iter().map(|entry| {
+ let (struct_ident, variant_ident) = parse_entry_pair(entry);
+ quote! {
+ Self::#variant_ident => {
+ // SAFETY: The `type_id` check ensures that `any` contains a value of type `#variant_ident`,
+ // so downcasting to `#variant_ident` is safe.
+ let value = unsafe { any.downcast::<#variant_ident>().unwrap_unchecked() };
+ <#struct_ident as ::mingling::Renderer>::render(value, __renderer_inner_result);
+ }
+ }
+ }).collect();
+ quote! {
+ fn render(any: ::mingling::AnyOutput<Self::Enum>, __renderer_inner_result: &mut ::mingling::RenderResult) {
+ match any.member_id {
+ #(#render_arms)*
+ _ => (),
+ }
+ }
+ }
+ };
+
+ // Build do_chain function (async and sync versions)
+ let chain_arms_async: Vec<_> = chain_tokens.iter().map(|entry| {
+ let (struct_ident, variant_ident) = parse_entry_pair(entry);
+ quote! {
+ Self::#variant_ident => {
+ // SAFETY: The `type_id` check ensures that `any` contains a value of type `#variant_ident`,
+ // so downcasting to `#variant_ident` is safe.
+ let value = unsafe { any.downcast::<#variant_ident>().unwrap_unchecked() };
+ let fut = async { <#struct_ident as ::mingling::Chain<Self::Enum>>::proc(value).await };
+ ::std::boxed::Box::pin(fut)
+ }
+ }
+ }).collect();
+
+ let chain_arms_sync: Vec<_> = chain_tokens
+ .iter()
+ .map(|entry| {
+ let (struct_ident, variant_ident) = parse_entry_pair(entry);
+ quote! {
+ Self::#variant_ident => {
+ // SAFETY: The `type_id` check ensures that `any` contains a value of type `#variant_ident`,
+ // so downcasting to `#variant_ident` is safe.
+ let value = unsafe { any.downcast::<#variant_ident>().unwrap_unchecked() };
+ <#struct_ident as ::mingling::Chain<Self::Enum>>::proc(value)
+ }
+ }
+ })
+ .collect();
+
+ let do_chain_fn = if chain_tokens.is_empty() {
+ quote! {
+ fn do_chain(_any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::ChainProcess<Self::Enum> {
+ ::core::panic!("No chain found for type id")
+ }
+ }
+ } else if ASYNC_ENABLED {
+ quote! {
+ fn do_chain(
+ any: ::mingling::AnyOutput<Self::Enum>,
+ ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::mingling::ChainProcess<Self::Enum>> + ::std::marker::Send>> {
+ match any.member_id {
+ #(#chain_arms_async)*
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ }
+ }
+ }
+ } else {
+ quote! {
+ fn do_chain(
+ any: ::mingling::AnyOutput<Self::Enum>,
+ ) -> ::mingling::ChainProcess<Self::Enum> {
+ match any.member_id {
+ #(#chain_arms_sync)*
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ }
+ }
+ }
+ };
+
let help_tokens: Vec<proc_macro2::TokenStream> = get_global_set(&HELP_REQUESTS)
.lock()
.unwrap()
@@ -1798,12 +1903,8 @@ pub fn program_final_gen(_input: TokenStream) -> TokenStream {
fn build_empty_result() -> ::mingling::AnyOutput<Self::Enum> {
::mingling::AnyOutput::new(ResultEmpty::new(()))
}
- ::mingling::__dispatch_program_renderers!(
- #(#renderer_tokens)*
- );
- ::mingling::__dispatch_program_chains!(
- #(#chain_tokens)*
- );
+ #render_fn
+ #do_chain_fn
fn render_help(any: ::mingling::AnyOutput<Self::Enum>, __renderer_inner_result: &mut ::mingling::RenderResult) {
match any.member_id {
#(#help_tokens)*