summaryrefslogtreecommitdiff
path: root/mingling_core/src/program
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-04-01 15:48:41 +0800
committer魏曹先生 <1992414357@qq.com>2026-04-01 15:48:41 +0800
commit3de10ca22cca06c4d9069984d0e66e370a331dde (patch)
tree7e8a9b035c360c016cde848b3442d3e1d5dcac5e /mingling_core/src/program
parentf3d6f76dfd07c35dabc11aa86d86c3671cd283c5 (diff)
Replace typeid-based dispatch with enum-based dispatch
- Add `Groupped` trait and `member_id` to `AnyOutput` - Add generic parameter `G` to `Dispatcher`, `Chain`, `Program` etc - Remove `hint` module and its marker types - Update macros to support explicit group specification - Add `gen_program` macro for generating enum-based programs - Add `GroupProcess` marker type for type-level grouping
Diffstat (limited to 'mingling_core/src/program')
-rw-r--r--mingling_core/src/program/exec.rs69
-rw-r--r--mingling_core/src/program/exec/error.rs12
-rw-r--r--mingling_core/src/program/flag.rs9
-rw-r--r--mingling_core/src/program/hint.rs62
-rw-r--r--mingling_core/src/program/setup.rs15
-rw-r--r--mingling_core/src/program/setup/basic.rs7
6 files changed, 67 insertions, 107 deletions
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 853bff1..cdbb2b0 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -1,42 +1,44 @@
#![allow(clippy::borrowed_box)]
+use std::fmt::Display;
+
use crate::{
AnyOutput, ChainProcess, Dispatcher, Next, Program, ProgramCollect, RenderResult,
error::ProgramInternalExecuteError,
- hint::{DispatcherNotFound, RendererNotFound},
};
pub mod error;
-pub async fn exec<C: ProgramCollect>(
- program: Program<C>,
-) -> Result<RenderResult, ProgramInternalExecuteError> {
+pub async fn exec<C, G>(program: Program<C, G>) -> Result<RenderResult, ProgramInternalExecuteError>
+where
+ C: ProgramCollect<Enum = G>,
+ G: Display,
+{
+ let mut current;
+
// Match user input
- let matched: (Box<dyn Dispatcher>, Vec<String>) = match match_user_input(&program) {
- Ok(r) => (r.0.clone(), r.1),
+ match match_user_input(&program) {
+ Ok((dispatcher, args)) => {
+ // Entry point
+ current = match dispatcher.begin(args) {
+ ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C, G>(any)),
+ ChainProcess::Ok((any, Next::Chain)) => any,
+ ChainProcess::Err(e) => return Err(e.into()),
+ };
+ }
Err(ProgramInternalExecuteError::DispatcherNotFound) => {
- // If no Dispatcher is found, dispatch to the DispatcherNotFound Dispatcher
- // to route it to the NoDispatcherFound struct
- let disp: Box<dyn Dispatcher> = Box::new(DispatcherNotFound);
- (disp, program.args)
+ // No matching Dispatcher is found
+ return Err(ProgramInternalExecuteError::DispatcherNotFound);
}
Err(e) => return Err(e),
};
- // Entry point
- let (dispatcher, args) = matched;
- let mut current = match dispatcher.begin(args) {
- ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C>(any)),
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
- };
-
loop {
current = {
// If a chain exists, execute as a chain
if C::has_chain(&current) {
match C::do_chain(current).await {
- ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C>(any)),
+ ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C, G>(any)),
ChainProcess::Ok((any, Next::Chain)) => any,
ChainProcess::Err(e) => return Err(e.into()),
}
@@ -47,29 +49,28 @@ pub async fn exec<C: ProgramCollect>(
C::render(current, &mut render_result);
return Ok(render_result);
}
- // If no renderer exists, transfer to the RendererNotFound Dispatcher for execution
+ // No renderer exists
else {
- let disp: Box<dyn Dispatcher> = Box::new(RendererNotFound);
-
- match disp.begin(vec![format!("{:?}", current.type_id)]) {
- ChainProcess::Ok((any, Next::Renderer)) => return Ok(render::<C>(any)),
- ChainProcess::Ok((any, Next::Chain)) => any,
- ChainProcess::Err(e) => return Err(e.into()),
- }
+ let renderer_name = current.member_id.to_string();
+ return Err(ProgramInternalExecuteError::RendererNotFound(renderer_name));
}
};
}
}
/// Match user input against registered dispatchers and return the matched dispatcher and remaining arguments.
-fn match_user_input<C: ProgramCollect>(
- program: &Program<C>,
-) -> Result<(&Box<dyn Dispatcher>, Vec<String>), ProgramInternalExecuteError> {
+fn match_user_input<C, G>(
+ program: &Program<C, G>,
+) -> Result<(&Box<dyn Dispatcher<G>>, Vec<String>), ProgramInternalExecuteError>
+where
+ C: ProgramCollect<Enum = G>,
+ G: Display,
+{
let nodes = get_nodes(program);
let command = format!("{} ", program.args.join(" "));
// Find all nodes that match the command prefix
- let matching_nodes: Vec<&(String, &Box<dyn Dispatcher>)> = nodes
+ let matching_nodes: Vec<&(String, &Box<dyn Dispatcher<G>>)> = nodes
.iter()
// Also add a space to the node string to ensure consistent matching logic
.filter(|(node_str, _)| command.starts_with(&format!("{} ", node_str)))
@@ -102,14 +103,16 @@ fn match_user_input<C: ProgramCollect>(
}
#[inline(always)]
-fn render<C: ProgramCollect>(any: AnyOutput) -> RenderResult {
+fn render<C: ProgramCollect<Enum = G>, G: Display>(any: AnyOutput<G>) -> RenderResult {
let mut render_result = RenderResult::default();
C::render(any, &mut render_result);
render_result
}
// Get all registered dispatcher names from the program
-fn get_nodes<C: ProgramCollect>(program: &Program<C>) -> Vec<(String, &Box<dyn Dispatcher>)> {
+fn get_nodes<C: ProgramCollect<Enum = G>, G: Display>(
+ program: &Program<C, G>,
+) -> Vec<(String, &Box<dyn Dispatcher<G>>)> {
program
.dispatcher
.iter()
diff --git a/mingling_core/src/program/exec/error.rs b/mingling_core/src/program/exec/error.rs
index fe66e22..b4ff378 100644
--- a/mingling_core/src/program/exec/error.rs
+++ b/mingling_core/src/program/exec/error.rs
@@ -5,6 +5,9 @@ pub enum ProgramExecuteError {
#[error("No Dispatcher Found")]
DispatcherNotFound,
+ #[error("No Renderer (`{0}`) Found")]
+ RendererNotFound(String),
+
#[error("Other error: {0}")]
Other(String),
}
@@ -14,6 +17,9 @@ pub enum ProgramInternalExecuteError {
#[error("No Dispatcher Found")]
DispatcherNotFound,
+ #[error("No Renderer (`{0}`) Found")]
+ RendererNotFound(String),
+
#[error("Other error: {0}")]
Other(String),
@@ -27,6 +33,9 @@ impl From<ProgramInternalExecuteError> for ProgramExecuteError {
ProgramInternalExecuteError::DispatcherNotFound => {
ProgramExecuteError::DispatcherNotFound
}
+ ProgramInternalExecuteError::RendererNotFound(s) => {
+ ProgramExecuteError::RendererNotFound(s)
+ }
ProgramInternalExecuteError::Other(s) => ProgramExecuteError::Other(s),
ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{}", e)),
}
@@ -38,9 +47,6 @@ impl From<ChainProcessError> for ProgramInternalExecuteError {
match value {
ChainProcessError::Other(s) => ProgramInternalExecuteError::Other(s),
ChainProcessError::IO(error) => ProgramInternalExecuteError::IO(error),
- ChainProcessError::Broken(_) => {
- ProgramInternalExecuteError::Other("Broken".to_string())
- }
}
}
}
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 74435e4..84fae01 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
use crate::{Program, ProgramCollect};
pub struct Flag {
@@ -88,14 +90,15 @@ macro_rules! special_argument {
}};
}
-impl<C> Program<C>
+impl<C, G> Program<C, G>
where
C: ProgramCollect,
+ G: Display,
{
/// Registers a global argument (with value) and its handler.
pub fn global_argument<F, A>(&mut self, arguments: A, do_fn: F)
where
- F: Fn(&mut Program<C>, String),
+ F: Fn(&mut Program<C, G>, String),
A: Into<Flag>,
{
let flag = arguments.into();
@@ -111,7 +114,7 @@ where
/// Registers a global flag (boolean) and its handler.
pub fn global_flag<F, A>(&mut self, flag: A, do_fn: F)
where
- F: Fn(&mut Program<C>),
+ F: Fn(&mut Program<C, G>),
A: Into<Flag>,
{
let flag = flag.into();
diff --git a/mingling_core/src/program/hint.rs b/mingling_core/src/program/hint.rs
deleted file mode 100644
index 6dbbac2..0000000
--- a/mingling_core/src/program/hint.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use crate::{AnyOutput, ChainProcess, Dispatcher, Node};
-
-/// Marker: Dispatcher Not Found
-///
-/// If a Dispatcher outputs NoDispatcherFound to the Chain,
-/// the program will terminate directly.
-///
-/// You can implement Renderer for NoDispatcherFound
-/// to render relevant information when a Dispatcher cannot be found.
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct NoDispatcherFound {
- pub args: Vec<String>,
-}
-
-#[derive(Default)]
-#[cfg_attr(feature = "general_renderer", derive(serde::Serialize))]
-pub struct DispatcherNotFound;
-impl Dispatcher for DispatcherNotFound {
- fn node(&self) -> crate::Node {
- Node::default().join("_not_found")
- }
-
- fn begin(&self, args: Vec<String>) -> ChainProcess {
- AnyOutput::new(NoDispatcherFound { args }).route_renderer()
- }
-
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher> {
- 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 = "general_renderer", derive(serde::Serialize))]
-pub struct NoRendererFound {
- pub type_to_render: String,
-}
-
-#[derive(Default)]
-#[cfg_attr(feature = "general_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.first().unwrap().clone(),
- })
- .route_renderer()
- }
-
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher> {
- Box::new(RendererNotFound)
- }
-}
diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs
index e81247e..1f16f80 100644
--- a/mingling_core/src/program/setup.rs
+++ b/mingling_core/src/program/setup.rs
@@ -1,18 +1,25 @@
+use std::fmt::Display;
+
use crate::{ProgramCollect, program::Program};
mod basic;
pub use basic::*;
-pub trait ProgramSetup<C: ProgramCollect> {
- fn setup(&mut self, program: &mut Program<C>);
+pub trait ProgramSetup<C, G>
+where
+ C: ProgramCollect,
+ G: Display,
+{
+ fn setup(&mut self, program: &mut Program<C, G>);
}
-impl<C> Program<C>
+impl<C, G> Program<C, G>
where
C: ProgramCollect,
+ G: Display,
{
/// Load and execute init logic
- pub fn with_setup<S: ProgramSetup<C> + 'static>(&mut self, mut setup: S) -> S {
+ pub fn with_setup<S: ProgramSetup<C, G> + 'static>(&mut self, mut setup: S) -> S {
S::setup(&mut setup, self);
setup
}
diff --git a/mingling_core/src/program/setup/basic.rs b/mingling_core/src/program/setup/basic.rs
index 43c14b9..8316a33 100644
--- a/mingling_core/src/program/setup/basic.rs
+++ b/mingling_core/src/program/setup/basic.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
use crate::{
ProgramCollect,
program::{Program, setup::ProgramSetup},
@@ -10,11 +12,12 @@ use crate::{
/// - Collects `--confirm` flag to skip user confirmation
pub struct BasicProgramSetup;
-impl<C> ProgramSetup<C> for BasicProgramSetup
+impl<C, G> ProgramSetup<C, G> for BasicProgramSetup
where
C: ProgramCollect,
+ G: Display,
{
- fn setup(&mut self, program: &mut Program<C>) {
+ fn setup(&mut self, program: &mut Program<C, G>) {
program.global_flag(["--quiet", "-q"], |p| {
p.stdout_setting.render_output = false;
p.stdout_setting.error_output = false;