diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-03-29 14:06:50 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-03-29 14:06:50 +0800 |
| commit | 5d91f0e9408e39afaa75f96b32c5ed7946a558f7 (patch) | |
| tree | 01ded24e80c1f07067adc882d547a69773d43c6d | |
| parent | 7809a8cbfbaf41fcc81de980c903e11f08bd1b12 (diff) | |
Add fallback dispatcher for missing renderers.
| -rw-r--r-- | mingling/src/lib.rs | 2 | ||||
| -rw-r--r-- | mingling/src/program.rs | 2 | ||||
| -rw-r--r-- | mingling/src/program/exec.rs | 33 | ||||
| -rw-r--r-- | mingling/src/program/hint.rs | 32 | ||||
| -rw-r--r-- | mingling_macros/src/chain.rs | 14 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 28 | ||||
| -rw-r--r-- | mingling_macros/src/renderer.rs | 14 |
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(¤t) { + 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(¤t) { + 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 |
