summaryrefslogtreecommitdiff
path: root/mingling/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-29 00:52:16 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-29 00:52:28 +0800
commitdb9afa0b06355028eafe3bc29fe0b2429ba8fd0a (patch)
tree60bf74d0853fcc0c1e9363813c26109a6ca38a4f /mingling/src
parent7ce68cd11516bd7cf037ecea99a92aee7c31b2c3 (diff)
Completed the first preliminary usable version of the Mingling
framework.
Diffstat (limited to 'mingling/src')
-rw-r--r--mingling/src/any.rs2
-rw-r--r--mingling/src/asset/dispatcher.rs192
-rw-r--r--mingling/src/lib.rs8
-rw-r--r--mingling/src/program.rs107
-rw-r--r--mingling/src/program/config.rs2
-rw-r--r--mingling/src/program/exec.rs130
-rw-r--r--mingling/src/program/exec/error.rs44
-rw-r--r--mingling/src/program/flag.rs41
-rw-r--r--mingling/src/program/hint.rs52
-rw-r--r--mingling/src/program/setup.rs16
-rw-r--r--mingling/src/program/setup/basic.rs12
11 files changed, 577 insertions, 29 deletions
diff --git a/mingling/src/any.rs b/mingling/src/any.rs
index a74cd54..5ff8cfe 100644
--- a/mingling/src/any.rs
+++ b/mingling/src/any.rs
@@ -8,7 +8,7 @@ pub type ChainProcess = Result<AnyOutput, ChainProcessError>;
#[derive(Debug)]
pub struct AnyOutput {
inner: Box<dyn std::any::Any + Send + 'static>,
- type_id: std::any::TypeId,
+ pub type_id: std::any::TypeId,
}
impl AnyOutput {
diff --git a/mingling/src/asset/dispatcher.rs b/mingling/src/asset/dispatcher.rs
index 31623d3..09bd386 100644
--- a/mingling/src/asset/dispatcher.rs
+++ b/mingling/src/asset/dispatcher.rs
@@ -1,19 +1,195 @@
use crate::{ChainProcess, Program, asset::node::Node};
-pub use mingling_macros::Dispatcher;
-
pub trait Dispatcher {
fn node(&self) -> Node;
+ fn begin(&self, args: Vec<String>) -> ChainProcess;
+ fn clone_dispatcher(&self) -> Box<dyn Dispatcher>;
}
-pub trait DispatcherChain {
- fn begin(&self) -> ChainProcess;
+impl Clone for Box<dyn Dispatcher> {
+ fn clone(&self) -> Self {
+ self.clone_dispatcher()
+ }
}
-impl Program {
+impl<C: crate::program::ProgramCollect> Program<C> {
/// Adds a dispatcher to the program.
- pub fn with_dispatcher<D: Dispatcher + 'static>(&mut self, dispatcher: D) {
- let dispatcher = Box::new(dispatcher);
- self.dispatcher.push(dispatcher);
+ pub fn with_dispatcher<D>(&mut self, dispatcher: D)
+ where
+ D: Into<Dispatchers>,
+ {
+ let dispatchers = dispatcher.into().dispatcher;
+ self.dispatcher.extend(dispatchers);
+ }
+}
+
+pub struct Dispatchers {
+ dispatcher: Vec<Box<dyn Dispatcher + 'static>>,
+}
+
+impl<D> From<D> for Dispatchers
+where
+ D: Dispatcher + 'static,
+{
+ fn from(dispatcher: D) -> Self {
+ Self {
+ dispatcher: vec![Box::new(dispatcher)],
+ }
+ }
+}
+
+impl From<Vec<Box<dyn Dispatcher>>> for Dispatchers {
+ fn from(dispatcher: Vec<Box<dyn Dispatcher>>) -> Self {
+ Self { dispatcher }
+ }
+}
+
+impl From<Box<dyn Dispatcher>> for Dispatchers {
+ fn from(dispatcher: Box<dyn Dispatcher>) -> Self {
+ Self {
+ dispatcher: vec![dispatcher],
+ }
+ }
+}
+
+impl<D> From<(D,)> for Dispatchers
+where
+ D: Dispatcher + 'static,
+{
+ fn from(dispatcher: (D,)) -> Self {
+ Self {
+ dispatcher: vec![Box::new(dispatcher.0)],
+ }
+ }
+}
+
+impl<D1, D2> From<(D1, D2)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2)) -> Self {
+ Self {
+ dispatcher: vec![Box::new(dispatchers.0), Box::new(dispatchers.1)],
+ }
+ }
+}
+
+impl<D1, D2, D3> From<(D1, D2, D3)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+ D3: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2, D3)) -> Self {
+ Self {
+ dispatcher: vec![
+ Box::new(dispatchers.0),
+ Box::new(dispatchers.1),
+ Box::new(dispatchers.2),
+ ],
+ }
+ }
+}
+
+impl<D1, D2, D3, D4> From<(D1, D2, D3, D4)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+ D3: Dispatcher + 'static,
+ D4: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2, D3, D4)) -> Self {
+ Self {
+ dispatcher: vec![
+ Box::new(dispatchers.0),
+ Box::new(dispatchers.1),
+ Box::new(dispatchers.2),
+ Box::new(dispatchers.3),
+ ],
+ }
+ }
+}
+
+impl<D1, D2, D3, D4, D5> From<(D1, D2, D3, D4, D5)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+ D3: Dispatcher + 'static,
+ D4: Dispatcher + 'static,
+ D5: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2, D3, D4, D5)) -> Self {
+ Self {
+ dispatcher: vec![
+ Box::new(dispatchers.0),
+ Box::new(dispatchers.1),
+ Box::new(dispatchers.2),
+ Box::new(dispatchers.3),
+ Box::new(dispatchers.4),
+ ],
+ }
+ }
+}
+
+impl<D1, D2, D3, D4, D5, D6> From<(D1, D2, D3, D4, D5, D6)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+ D3: Dispatcher + 'static,
+ D4: Dispatcher + 'static,
+ D5: Dispatcher + 'static,
+ D6: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2, D3, D4, D5, D6)) -> Self {
+ Self {
+ dispatcher: vec![
+ Box::new(dispatchers.0),
+ Box::new(dispatchers.1),
+ Box::new(dispatchers.2),
+ Box::new(dispatchers.3),
+ Box::new(dispatchers.4),
+ Box::new(dispatchers.5),
+ ],
+ }
+ }
+}
+
+impl<D1, D2, D3, D4, D5, D6, D7> From<(D1, D2, D3, D4, D5, D6, D7)> for Dispatchers
+where
+ D1: Dispatcher + 'static,
+ D2: Dispatcher + 'static,
+ D3: Dispatcher + 'static,
+ D4: Dispatcher + 'static,
+ D5: Dispatcher + 'static,
+ D6: Dispatcher + 'static,
+ D7: Dispatcher + 'static,
+{
+ fn from(dispatchers: (D1, D2, D3, D4, D5, D6, D7)) -> Self {
+ Self {
+ dispatcher: vec![
+ Box::new(dispatchers.0),
+ Box::new(dispatchers.1),
+ Box::new(dispatchers.2),
+ Box::new(dispatchers.3),
+ Box::new(dispatchers.4),
+ Box::new(dispatchers.5),
+ Box::new(dispatchers.6),
+ ],
+ }
+ }
+}
+
+impl std::ops::Deref for Dispatchers {
+ type Target = Vec<Box<dyn Dispatcher + 'static>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.dispatcher
+ }
+}
+
+impl Into<Vec<Box<dyn Dispatcher + 'static>>> for Dispatchers {
+ fn into(self) -> Vec<Box<dyn Dispatcher + 'static>> {
+ self.dispatcher
}
}
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index c172559..060fa30 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -3,6 +3,7 @@ pub use crate::any::*;
pub mod error {
pub use crate::asset::chain::error::*;
+ pub use crate::exec::error::*;
}
mod program;
@@ -10,13 +11,19 @@ pub use crate::program::*;
pub mod setup {
pub use crate::program::setup::*;
}
+pub mod hint {
+ pub use crate::program::hint::*;
+}
#[cfg(feature = "macros")]
#[allow(unused_imports)]
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_render;
pub use mingling_macros::node;
+ pub use mingling_macros::program;
pub use mingling_macros::r_print;
pub use mingling_macros::r_println;
pub use mingling_macros::renderer;
@@ -30,4 +37,3 @@ pub use crate::asset::dispatcher::*;
pub use crate::asset::node::*;
pub use crate::asset::renderer::*;
pub use crate::renderer::render_result::*;
-pub use mingling_macros::Dispatcher;
diff --git a/mingling/src/program.rs b/mingling/src/program.rs
index e4bc3d0..5e9d1f2 100644
--- a/mingling/src/program.rs
+++ b/mingling/src/program.rs
@@ -1,6 +1,11 @@
-use crate::asset::dispatcher::Dispatcher;
-use std::env;
+use crate::{
+ AnyOutput, ChainProcess, RenderResult, asset::dispatcher::Dispatcher,
+ error::ProgramExecuteError,
+};
+use std::{env, pin::Pin};
+pub mod exec;
+pub mod hint;
pub mod setup;
mod config;
@@ -8,9 +13,12 @@ pub use config::*;
mod flag;
pub use flag::*;
+use tokio::io::AsyncWriteExt;
#[derive(Default)]
-pub struct Program {
+pub struct Program<C: ProgramCollect> {
+ pub(crate) collect: std::marker::PhantomData<C>,
+
pub(crate) args: Vec<String>,
pub(crate) dispatcher: Vec<Box<dyn Dispatcher>>,
@@ -18,18 +26,105 @@ pub struct Program {
pub user_context: ProgramUserContext,
}
-impl Program {
+impl<C> Program<C>
+where
+ C: ProgramCollect,
+{
/// Creates a new Program instance, initializing args from environment.
pub fn new() -> Self {
Program {
+ collect: std::marker::PhantomData,
args: env::args().collect(),
dispatcher: Vec::new(),
- ..Default::default()
+ stdout_setting: Default::default(),
+ user_context: Default::default(),
}
}
/// Run the command line program
+ pub async fn exec_without_render(mut self) -> Result<RenderResult, ProgramExecuteError> {
+ self.args = self.args.iter().skip(1).cloned().collect();
+ crate::exec::exec(self).await.map_err(|e| e.into())
+ }
+
+ /// Run the command line program
pub async fn exec(self) {
- todo!()
+ let stdout_setting = self.stdout_setting.clone();
+ let result = match self.exec_without_render().await {
+ Ok(r) => r,
+ Err(e) => match e {
+ ProgramExecuteError::DispatcherNotFound => {
+ eprintln!("Dispatcher not found");
+ return;
+ }
+ ProgramExecuteError::Other(e) => {
+ eprintln!("{}", e);
+ return;
+ }
+ },
+ };
+
+ // Render result
+ if stdout_setting.render_output {
+ if !result.is_empty() {
+ print!("{}", result);
+ if let Err(e) = tokio::io::stdout().flush().await
+ && stdout_setting.error_output
+ {
+ eprintln!("{}", e.to_string());
+ }
+ }
+ }
}
}
+
+pub trait ProgramCollect {
+ fn render(any: AnyOutput, r: &mut RenderResult);
+ fn do_chain(any: AnyOutput) -> Pin<Box<dyn Future<Output = ChainProcess> + Send>>;
+}
+
+#[macro_export]
+macro_rules! __dispatch_program_renderers {
+ (
+ $( $render_ty:ty => $prev_ty:ty, )*
+ ) => {
+ fn render(any: mingling::AnyOutput, r: &mut mingling::RenderResult) {
+ match any.type_id {
+ $(
+ id if id == std::any::TypeId::of::<$prev_ty>() => {
+ let value = any.downcast::<$prev_ty>().unwrap();
+ <$render_ty as mingling::Renderer>::render(value, r);
+ }
+ )*
+ _ => (),
+ }
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! __dispatch_program_chains {
+ (
+ $( $chain_ty:ty => $chain_prev:ty, )*
+ ) => {
+ fn do_chain(
+ any: mingling::AnyOutput,
+ ) -> std::pin::Pin<Box<dyn Future<Output = mingling::ChainProcess> + Send>> {
+ match any.type_id {
+ $(
+ id if id == std::any::TypeId::of::<$chain_prev>() => {
+ let value = any.downcast::<$chain_prev>().unwrap();
+ let fut = async { <$chain_ty as mingling::Chain>::proc(value).await };
+ Box::pin(fut)
+ }
+ )*
+ _ => Box::pin(async move {
+ mingling::AnyOutput::new(mingling::hint::NoChainFound {
+ name: format!("{:?}", any.type_id).to_string(),
+ })
+ .route_chain()
+ }),
+ }
+ }
+ };
+}
diff --git a/mingling/src/program/config.rs b/mingling/src/program/config.rs
index 93507e7..ffcade3 100644
--- a/mingling/src/program/config.rs
+++ b/mingling/src/program/config.rs
@@ -1,3 +1,4 @@
+#[derive(Debug, Clone)]
pub struct ProgramStdoutSetting {
/// Output error messages
pub error_output: bool,
@@ -15,6 +16,7 @@ impl Default for ProgramStdoutSetting {
}
}
+#[derive(Debug, Clone)]
pub struct ProgramUserContext {
/// View help information instead of running the command
pub help: bool,
diff --git a/mingling/src/program/exec.rs b/mingling/src/program/exec.rs
new file mode 100644
index 0000000..99eb419
--- /dev/null
+++ b/mingling/src/program/exec.rs
@@ -0,0 +1,130 @@
+use crate::{
+ AnyOutput, ChainProcess, Dispatcher, Program, ProgramCollect, RenderResult,
+ error::{ChainProcessError, ProgramInternalExecuteError},
+ hint::{DispatcherNotFound, NoChainFound, ProgramEnd},
+};
+
+pub mod error;
+
+pub async fn exec<C: ProgramCollect>(
+ program: Program<C>,
+) -> Result<RenderResult, ProgramInternalExecuteError> {
+ // Match user input
+ let matched: (Box<dyn Dispatcher>, Vec<String>) = match match_user_input(&program) {
+ Ok(r) => (r.0.clone(), r.1),
+ 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)
+ }
+ Err(e) => return Err(e),
+ };
+
+ // Entry point
+ let (dispatcher, args) = matched;
+ let mut current = match handle_chain_process::<C>(dispatcher.begin(args)) {
+ Ok(Next::RenderResult(render_result)) => return Ok(render_result),
+ Ok(Next::AnyOutput(any)) => any,
+ Err(e) => return Err(e),
+ };
+
+ 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),
+ };
+ if current.is::<ProgramEnd>() || current.is::<NoChainFound>() {
+ break;
+ }
+ }
+
+ Ok(RenderResult::default())
+}
+
+/// 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> {
+ 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
+ .iter()
+ // Also add a space to the node string to ensure consistent matching logic
+ .filter(|(node_str, _)| command.starts_with(&format!("{} ", node_str)))
+ .collect();
+
+ match matching_nodes.len() {
+ 0 => {
+ // No matching node found
+ Err(ProgramInternalExecuteError::DispatcherNotFound)
+ }
+ 1 => {
+ let matched_prefix = matching_nodes[0];
+ let prefix_len = matched_prefix.0.split_whitespace().count();
+ let trimmed_args: Vec<String> = program.args.iter().skip(prefix_len).cloned().collect();
+ Ok((matched_prefix.1, trimmed_args))
+ }
+ _ => {
+ // Multiple matching nodes found
+ // Find the node with the longest length (most specific match)
+ let matched_prefix = matching_nodes
+ .iter()
+ .max_by_key(|node| node.0.len())
+ .unwrap();
+
+ let prefix_len = matched_prefix.0.split_whitespace().count();
+ let trimmed_args: Vec<String> = program.args.iter().skip(prefix_len).cloned().collect();
+ Ok((matched_prefix.1, trimmed_args))
+ }
+ }
+}
+
+#[inline(always)]
+fn render<C: ProgramCollect>(any: AnyOutput) -> RenderResult {
+ let mut render_result = RenderResult::default();
+ C::render(any, &mut render_result);
+ render_result
+}
+
+fn handle_chain_process<C: ProgramCollect>(
+ process: ChainProcess,
+) -> Result<Next, ProgramInternalExecuteError> {
+ match process {
+ Ok(any) => Ok(Next::AnyOutput(any)),
+ Err(e) => match e {
+ ChainProcessError::Broken(any_output) => {
+ let render_result = render::<C>(any_output);
+ return Ok(Next::RenderResult(render_result));
+ }
+ _ => {
+ return Err(e.into());
+ }
+ },
+ }
+}
+
+// Get all registered dispatcher names from the program
+fn get_nodes<C: ProgramCollect>(program: &Program<C>) -> Vec<(String, &Box<dyn Dispatcher>)> {
+ program
+ .dispatcher
+ .iter()
+ .map(|disp| {
+ let node_str = disp
+ .node()
+ .to_string()
+ .split('.')
+ .collect::<Vec<_>>()
+ .join(" ");
+ (node_str, disp)
+ })
+ .collect()
+}
+
+enum Next {
+ RenderResult(RenderResult),
+ AnyOutput(AnyOutput),
+}
diff --git a/mingling/src/program/exec/error.rs b/mingling/src/program/exec/error.rs
new file mode 100644
index 0000000..0523ea5
--- /dev/null
+++ b/mingling/src/program/exec/error.rs
@@ -0,0 +1,44 @@
+use crate::error::ChainProcessError;
+
+#[derive(thiserror::Error, Debug)]
+pub enum ProgramExecuteError {
+ #[error("No Dispatcher Found")]
+ DispatcherNotFound,
+
+ #[error("Other error: {0}")]
+ Other(String),
+}
+
+#[derive(thiserror::Error, Debug)]
+pub enum ProgramInternalExecuteError {
+ #[error("No Dispatcher Found")]
+ DispatcherNotFound,
+
+ #[error("Other error: {0}")]
+ Other(String),
+
+ #[error("IO error: {0}")]
+ IO(#[from] std::io::Error),
+}
+
+impl From<ProgramInternalExecuteError> for ProgramExecuteError {
+ fn from(value: ProgramInternalExecuteError) -> Self {
+ match value {
+ ProgramInternalExecuteError::DispatcherNotFound => {
+ ProgramExecuteError::DispatcherNotFound
+ }
+ ProgramInternalExecuteError::Other(s) => ProgramExecuteError::Other(s),
+ ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{}", e)),
+ }
+ }
+}
+
+impl From<ChainProcessError> for ProgramInternalExecuteError {
+ fn from(value: ChainProcessError) -> Self {
+ match value {
+ ChainProcessError::Other(s) => ProgramInternalExecuteError::Other(s),
+ ChainProcessError::IO(error) => ProgramInternalExecuteError::IO(error),
+ ChainProcessError::Broken(_) => ProgramInternalExecuteError::Other(format!("Broken")),
+ }
+ }
+}
diff --git a/mingling/src/program/flag.rs b/mingling/src/program/flag.rs
index 0178ebe..8372517 100644
--- a/mingling/src/program/flag.rs
+++ b/mingling/src/program/flag.rs
@@ -1,4 +1,4 @@
-use crate::Program;
+use crate::{Program, ProgramCollect};
pub struct Flag {
vec: Vec<&'static str>,
@@ -80,11 +80,14 @@ macro_rules! special_argument {
}};
}
-impl Program {
+impl<C> Program<C>
+where
+ C: ProgramCollect,
+{
/// 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, String),
+ F: Fn(&mut Program<C>, String),
A: Into<Flag>,
{
let flag = arguments.into();
@@ -100,7 +103,7 @@ impl Program {
/// 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),
+ F: Fn(&mut Program<C>),
A: Into<Flag>,
{
let flag = flag.into();
@@ -112,4 +115,34 @@ impl Program {
}
}
}
+
+ /// Extracts a global argument (with value) from arguments
+ pub fn pick_global_argument<F>(&mut self, flag: F) -> Option<String>
+ where
+ F: Into<Flag>,
+ {
+ let flag: Flag = flag.into();
+ for argument in flag.iter() {
+ let value = special_argument!(self.args, argument);
+ if value.is_some() {
+ return value;
+ }
+ }
+ None
+ }
+
+ /// Extracts global flags from arguments
+ pub fn pick_global_flag<F>(&mut self, flag: F) -> bool
+ where
+ F: Into<Flag>,
+ {
+ let flag: Flag = flag.into();
+ for argument in flag.iter() {
+ let enabled = special_flag!(self.args, argument);
+ if enabled {
+ return enabled;
+ }
+ }
+ return false;
+ }
}
diff --git a/mingling/src/program/hint.rs b/mingling/src/program/hint.rs
new file mode 100644
index 0000000..e9c510b
--- /dev/null
+++ b/mingling/src/program/hint.rs
@@ -0,0 +1,52 @@
+use crate::{AnyOutput, ChainProcess, Dispatcher, Node};
+
+/// Marker: Program End
+///
+/// If a chain outputs ProgramEnd to the Chain,
+/// the program will terminate directly.
+///
+/// You can implement Renderer for ProgramEnd
+/// to render relevant information after the program ends.
+#[cfg_attr(feature = "serde_renderer", derive(serde::Serialize))]
+pub struct ProgramEnd;
+
+/// Marker: Chain Not Found
+///
+/// If a Chain or Dispatcher outputs NoChainFound to the Chain,
+/// the program will terminate directly.
+///
+/// You can implement Renderer for NoChainFound
+/// to render relevant information when a Chain cannot be found.
+#[cfg_attr(feature = "serde_renderer", derive(serde::Serialize))]
+pub struct NoChainFound {
+ pub name: String,
+}
+
+/// 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 = "serde_renderer", derive(serde::Serialize))]
+pub struct NoDispatcherFound {
+ pub args: Vec<String>,
+}
+
+#[derive(Default)]
+#[cfg_attr(feature = "serde_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)
+ }
+}
diff --git a/mingling/src/program/setup.rs b/mingling/src/program/setup.rs
index fdf7b04..e81247e 100644
--- a/mingling/src/program/setup.rs
+++ b/mingling/src/program/setup.rs
@@ -1,15 +1,19 @@
-use crate::program::Program;
+use crate::{ProgramCollect, program::Program};
mod basic;
pub use basic::*;
-pub trait ProgramSetup {
- fn setup(program: &mut Program);
+pub trait ProgramSetup<C: ProgramCollect> {
+ fn setup(&mut self, program: &mut Program<C>);
}
-impl Program {
+impl<C> Program<C>
+where
+ C: ProgramCollect,
+{
/// Load and execute init logic
- pub fn with_setup<S: ProgramSetup + 'static>(&mut self, _setup: S) {
- S::setup(self);
+ pub fn with_setup<S: ProgramSetup<C> + 'static>(&mut self, mut setup: S) -> S {
+ S::setup(&mut setup, self);
+ setup
}
}
diff --git a/mingling/src/program/setup/basic.rs b/mingling/src/program/setup/basic.rs
index 0ea72a6..43c14b9 100644
--- a/mingling/src/program/setup/basic.rs
+++ b/mingling/src/program/setup/basic.rs
@@ -1,4 +1,7 @@
-use crate::program::{Program, setup::ProgramSetup};
+use crate::{
+ ProgramCollect,
+ program::{Program, setup::ProgramSetup},
+};
/// Performs basic program initialization:
///
@@ -7,8 +10,11 @@ use crate::program::{Program, setup::ProgramSetup};
/// - Collects `--confirm` flag to skip user confirmation
pub struct BasicProgramSetup;
-impl ProgramSetup for BasicProgramSetup {
- fn setup(program: &mut Program) {
+impl<C> ProgramSetup<C> for BasicProgramSetup
+where
+ C: ProgramCollect,
+{
+ fn setup(&mut self, program: &mut Program<C>) {
program.global_flag(["--quiet", "-q"], |p| {
p.stdout_setting.render_output = false;
p.stdout_setting.error_output = false;