summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-29 14:06:50 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-29 14:06:50 +0800
commit5d91f0e9408e39afaa75f96b32c5ed7946a558f7 (patch)
tree01ded24e80c1f07067adc882d547a69773d43c6d
parent7809a8cbfbaf41fcc81de980c903e11f08bd1b12 (diff)
Add fallback dispatcher for missing renderers.
-rw-r--r--mingling/src/lib.rs2
-rw-r--r--mingling/src/program.rs2
-rw-r--r--mingling/src/program/exec.rs33
-rw-r--r--mingling/src/program/hint.rs32
-rw-r--r--mingling_macros/src/chain.rs14
-rw-r--r--mingling_macros/src/lib.rs28
-rw-r--r--mingling_macros/src/renderer.rs14
7 files changed, 112 insertions, 13 deletions
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 060fa30..4d9bcc5 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -20,7 +20,7 @@ pub mod hint {
pub mod macros {
pub use mingling_macros::chain;
pub use mingling_macros::chain_struct;
- pub use mingling_macros::dispatcher_chain;
+ pub use mingling_macros::dispatcher;
pub use mingling_macros::dispatcher_render;
pub use mingling_macros::node;
pub use mingling_macros::program;
diff --git a/mingling/src/program.rs b/mingling/src/program.rs
index 5e9d1f2..3394a38 100644
--- a/mingling/src/program.rs
+++ b/mingling/src/program.rs
@@ -81,6 +81,8 @@ where
pub trait ProgramCollect {
fn render(any: AnyOutput, r: &mut RenderResult);
fn do_chain(any: AnyOutput) -> Pin<Box<dyn Future<Output = ChainProcess> + Send>>;
+ fn has_renderer(any: &AnyOutput) -> bool;
+ fn has_chain(any: &AnyOutput) -> bool;
}
#[macro_export]
diff --git a/mingling/src/program/exec.rs b/mingling/src/program/exec.rs
index 99eb419..04ed16d 100644
--- a/mingling/src/program/exec.rs
+++ b/mingling/src/program/exec.rs
@@ -1,7 +1,7 @@
use crate::{
AnyOutput, ChainProcess, Dispatcher, Program, ProgramCollect, RenderResult,
error::{ChainProcessError, ProgramInternalExecuteError},
- hint::{DispatcherNotFound, NoChainFound, ProgramEnd},
+ hint::{DispatcherNotFound, NoChainFound, ProgramEnd, RendererNotFound},
};
pub mod error;
@@ -30,10 +30,33 @@ pub async fn exec<C: ProgramCollect>(
};
loop {
- current = match handle_chain_process::<C>(C::do_chain(current).await) {
- Ok(Next::RenderResult(render_result)) => return Ok(render_result),
- Ok(Next::AnyOutput(any)) => any,
- Err(e) => return Err(e),
+ current = {
+ // If a chain exists, execute as a chain
+ if C::has_chain(&current) {
+ match handle_chain_process::<C>(C::do_chain(current).await) {
+ Ok(Next::RenderResult(render_result)) => return Ok(render_result),
+ Ok(Next::AnyOutput(any)) => any,
+ Err(e) => return Err(e),
+ }
+ }
+ // If no chain exists, attempt to render
+ else if C::has_renderer(&current) {
+ let mut render_result = RenderResult::default();
+ C::render(current, &mut render_result);
+ return Ok(render_result);
+ }
+ // If no renderer exists, transfer to the RendererNotFound Dispatcher for execution
+ else {
+ let disp: Box<dyn Dispatcher> = Box::new(RendererNotFound);
+ let any = match handle_chain_process::<C>(
+ disp.begin(vec![format!("{:?}", current.type_id)]),
+ ) {
+ Ok(Next::AnyOutput(any)) => any,
+ Ok(Next::RenderResult(result)) => return Ok(result),
+ Err(e) => return Err(e),
+ };
+ any
+ }
};
if current.is::<ProgramEnd>() || current.is::<NoChainFound>() {
break;
diff --git a/mingling/src/program/hint.rs b/mingling/src/program/hint.rs
index e9c510b..bf35fea 100644
--- a/mingling/src/program/hint.rs
+++ b/mingling/src/program/hint.rs
@@ -50,3 +50,35 @@ impl Dispatcher for DispatcherNotFound {
Box::new(DispatcherNotFound)
}
}
+
+/// Marker: Renderer Not Found
+///
+/// If a Chain outputs NoRendererFound to the Chain,
+/// the program will terminate directly.
+///
+/// You can implement Renderer for NoRendererFound
+/// to render relevant information when a Renderer cannot be found.
+#[cfg_attr(feature = "serde_renderer", derive(serde::Serialize))]
+pub struct NoRendererFound {
+ pub type_to_render: String,
+}
+
+#[derive(Default)]
+#[cfg_attr(feature = "serde_renderer", derive(serde::Serialize))]
+pub struct RendererNotFound;
+impl Dispatcher for RendererNotFound {
+ fn node(&self) -> crate::Node {
+ Node::default().join("_not_found")
+ }
+
+ fn begin(&self, args: Vec<String>) -> ChainProcess {
+ AnyOutput::new(NoRendererFound {
+ type_to_render: args.get(0).unwrap().clone(),
+ })
+ .route_renderer()
+ }
+
+ fn clone_dispatcher(&self) -> Box<dyn Dispatcher> {
+ Box::new(RendererNotFound)
+ }
+}
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs
index a7be233..9ef4ae0 100644
--- a/mingling_macros/src/chain.rs
+++ b/mingling_macros/src/chain.rs
@@ -125,10 +125,18 @@ pub fn chain_attr(item: TokenStream) -> TokenStream {
let chain_entry = quote! {
#struct_name => #previous_type,
};
+ let chain_exist_entry = quote! {
+ id if id == std::any::TypeId::of::<#previous_type>() => true,
+ };
let mut chains = crate::CHAINS.lock().unwrap();
- let entry = chain_entry.to_string();
- if !chains.contains(&entry) {
- chains.push(entry);
+ 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();
+ if !chains.contains(&chain_entry) {
+ chains.push(chain_entry);
+ }
+ if !chains.contains(&chain_exist_entry) {
+ chain_exist.push(chain_exist_entry);
}
expanded.into()
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index 26a7381..821743e 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -21,6 +21,8 @@ use std::sync::Mutex;
// Global variable declarations for storing chain and renderer mappings
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()));
+pub(crate) static CHAINS_EXIST: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
+pub(crate) static RENDERERS_EXIST: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
/// Creates a command node from a dot-separated path string.
///
@@ -66,7 +68,7 @@ pub fn chain_struct(input: TokenStream) -> TokenStream {
}
#[proc_macro]
-pub fn dispatcher_chain(input: TokenStream) -> TokenStream {
+pub fn dispatcher(input: TokenStream) -> TokenStream {
dispatcher_chain::dispatcher_chain(input)
}
@@ -207,6 +209,8 @@ pub fn program(input: TokenStream) -> TokenStream {
let renderers = RENDERERS.lock().unwrap().clone();
let chains = CHAINS.lock().unwrap().clone();
+ let renderer_exist = RENDERERS_EXIST.lock().unwrap().clone();
+ let chain_exist = CHAINS_EXIST.lock().unwrap().clone();
let renderer_tokens: Vec<proc_macro2::TokenStream> = renderers
.iter()
@@ -218,6 +222,16 @@ pub fn program(input: TokenStream) -> TokenStream {
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
.collect();
+ let renderer_exist_tokens: Vec<proc_macro2::TokenStream> = renderer_exist
+ .iter()
+ .map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
+ .collect();
+
+ let chain_exist_tokens: Vec<proc_macro2::TokenStream> = chain_exist
+ .iter()
+ .map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
+ .collect();
+
let expanded = quote! {
pub struct #name;
@@ -228,6 +242,18 @@ pub fn program(input: TokenStream) -> TokenStream {
::mingling::__dispatch_program_chains!(
#(#chain_tokens)*
);
+ fn has_renderer(any: &::mingling::AnyOutput) -> bool {
+ match any.type_id {
+ #(#renderer_exist_tokens)*
+ _ => false
+ }
+ }
+ fn has_chain(any: &::mingling::AnyOutput) -> bool {
+ match any.type_id {
+ #(#chain_exist_tokens)*
+ _ => false
+ }
+ }
}
impl #name {
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index 54a2526..de244c0 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -104,11 +104,19 @@ pub fn renderer_attr(item: TokenStream) -> TokenStream {
let renderer_entry = quote! {
#struct_name => #previous_type,
};
+ let renderer_exist_entry = quote! {
+ id if id == std::any::TypeId::of::<#previous_type>() => true,
+ };
let mut renderers = crate::RENDERERS.lock().unwrap();
- let entry_str = renderer_entry.to_string();
- if !renderers.contains(&entry_str) {
- renderers.push(entry_str);
+ let mut renderer_exist = crate::RENDERERS_EXIST.lock().unwrap();
+ let renderer_entry_str = renderer_entry.to_string();
+ let renderer_exist_entry_str = renderer_exist_entry.to_string();
+ if !renderers.contains(&renderer_entry_str) {
+ renderers.push(renderer_entry_str);
+ }
+ if !renderer_exist.contains(&renderer_exist_entry_str) {
+ renderer_exist.push(renderer_exist_entry_str);
}
// Generate the struct and implementation