use std::fmt::Display; use crate::{ChainProcess, Program, ProgramCollect, asset::node::Node}; /// Dispatches user input commands to specific [`ChainProcess`](./enum.ChainProcess.html) /// /// Note: If you are using [mingling_macros](https://crates.io/crates/mingling_macros), /// you can use the `dispatcher!("node.subnode", CommandType => Entry)` macro to declare a `Dispatcher` pub trait Dispatcher { /// Returns a command node for matching user input fn node(&self) -> Node; /// Returns a [`ChainProcess`](./enum.ChainProcess.html) based on user input arguments, /// to be sent to the specific invocation fn begin(&self, args: Vec) -> ChainProcess; /// Clones the current dispatcher for implementing the `Clone` trait fn clone_dispatcher(&self) -> Box>; } impl Clone for Box> where G: Display, { fn clone(&self) -> Self { self.clone_dispatcher() } } impl Program where C: ProgramCollect, { /// Adds a dispatcher to the program. #[cfg(not(feature = "dispatch_tree"))] pub fn with_dispatcher(&mut self, dispatcher: Disp) where Disp: Dispatcher + Send + Sync + 'static, { self.dispatcher.push(Box::new(dispatcher)); } /// Add some dispatchers to the program. #[cfg(not(feature = "dispatch_tree"))] pub fn with_dispatchers(&mut self, dispatchers: D) where D: Into>, { let dispatchers = dispatchers.into(); self.dispatcher.extend(dispatchers.dispatcher); } } /// A collection of dispatchers. /// /// This struct holds a vector of boxed `Dispatcher` trait objects, /// allowing multiple dispatchers to be grouped together and passed /// to the program via `Program::with_dispatchers`. /// A collection of dispatchers. /// /// This struct holds a vector of boxed `Dispatcher` trait objects, /// allowing multiple dispatchers to be grouped together and passed /// to the program via `Program::with_dispatchers`. pub struct Dispatchers { dispatcher: Vec + Send + Sync + 'static>>, } impl From + Send + Sync>>> for Dispatchers { fn from(dispatcher: Vec + Send + Sync>>) -> Self { Self { dispatcher } } } impl From + Send + Sync>> for Dispatchers { fn from(dispatcher: Box + Send + Sync>) -> Self { Self { dispatcher: vec![dispatcher], } } } impl From<(D,)> for Dispatchers where D: Dispatcher + Send + Sync + 'static, G: Display, { fn from(dispatcher: (D,)) -> Self { Self { dispatcher: vec![Box::new(dispatcher.0)], } } } impl From<(D1, D2)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, G: Display, { fn from(dispatchers: (D1, D2)) -> Self { Self { dispatcher: vec![Box::new(dispatchers.0), Box::new(dispatchers.1)], } } } impl From<(D1, D2, D3)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, D3: Dispatcher + Send + Sync + 'static, G: Display, { fn from(dispatchers: (D1, D2, D3)) -> Self { Self { dispatcher: vec![ Box::new(dispatchers.0), Box::new(dispatchers.1), Box::new(dispatchers.2), ], } } } impl From<(D1, D2, D3, D4)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, D3: Dispatcher + Send + Sync + 'static, D4: Dispatcher + Send + Sync + 'static, G: Display, { 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 From<(D1, D2, D3, D4, D5)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, D3: Dispatcher + Send + Sync + 'static, D4: Dispatcher + Send + Sync + 'static, D5: Dispatcher + Send + Sync + 'static, G: Display, { 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 From<(D1, D2, D3, D4, D5, D6)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, D3: Dispatcher + Send + Sync + 'static, D4: Dispatcher + Send + Sync + 'static, D5: Dispatcher + Send + Sync + 'static, D6: Dispatcher + Send + Sync + 'static, G: Display, { 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 From<(D1, D2, D3, D4, D5, D6, D7)> for Dispatchers where D1: Dispatcher + Send + Sync + 'static, D2: Dispatcher + Send + Sync + 'static, D3: Dispatcher + Send + Sync + 'static, D4: Dispatcher + Send + Sync + 'static, D5: Dispatcher + Send + Sync + 'static, D6: Dispatcher + Send + Sync + 'static, D7: Dispatcher + Send + Sync + 'static, G: Display, { 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 + Send + Sync + 'static>>; fn deref(&self) -> &Self::Target { &self.dispatcher } } impl From> for Vec + Send + Sync + 'static>> { fn from(val: Dispatchers) -> Self { val.dispatcher } } #[cfg(test)] mod tests { use super::*; use crate::ChainProcess; use std::fmt::Display; /// A minimal mock Dispatcher for testing Dispatchers conversions. #[derive(Clone)] struct MockDispatcher { name: &'static str, } impl Dispatcher for MockDispatcher { fn node(&self) -> crate::asset::node::Node { self.name.into() } fn begin(&self, _args: Vec) -> ChainProcess { unimplemented!("not used in these tests") } fn clone_dispatcher(&self) -> Box> { Box::new(self.clone()) } } /// Minimal mock group for Dispatchers tests #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(dead_code)] enum MockG { A, } impl Display for MockG { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "A") } } #[test] fn test_dispatchers_from_single_tuple() { let disp = MockDispatcher { name: "foo" }; let dispatchers: Dispatchers = Dispatchers::from((disp,)); assert_eq!(dispatchers.dispatcher.len(), 1); } #[test] fn test_dispatchers_from_two_tuple() { let d1 = MockDispatcher { name: "a" }; let d2 = MockDispatcher { name: "b" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2)); assert_eq!(dispatchers.dispatcher.len(), 2); } #[test] fn test_dispatchers_from_three_tuple() { let d1 = MockDispatcher { name: "x" }; let d2 = MockDispatcher { name: "y" }; let d3 = MockDispatcher { name: "z" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2, d3)); assert_eq!(dispatchers.dispatcher.len(), 3); } #[test] fn test_dispatchers_from_four_tuple() { let d1 = MockDispatcher { name: "1" }; let d2 = MockDispatcher { name: "2" }; let d3 = MockDispatcher { name: "3" }; let d4 = MockDispatcher { name: "4" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2, d3, d4)); assert_eq!(dispatchers.dispatcher.len(), 4); } #[test] fn test_dispatchers_from_five_tuple() { let d1 = MockDispatcher { name: "a" }; let d2 = MockDispatcher { name: "b" }; let d3 = MockDispatcher { name: "c" }; let d4 = MockDispatcher { name: "d" }; let d5 = MockDispatcher { name: "e" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2, d3, d4, d5)); assert_eq!(dispatchers.dispatcher.len(), 5); } #[test] fn test_dispatchers_from_six_tuple() { let d1 = MockDispatcher { name: "a" }; let d2 = MockDispatcher { name: "b" }; let d3 = MockDispatcher { name: "c" }; let d4 = MockDispatcher { name: "d" }; let d5 = MockDispatcher { name: "e" }; let d6 = MockDispatcher { name: "f" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2, d3, d4, d5, d6)); assert_eq!(dispatchers.dispatcher.len(), 6); } #[test] fn test_dispatchers_from_seven_tuple() { let d1 = MockDispatcher { name: "a" }; let d2 = MockDispatcher { name: "b" }; let d3 = MockDispatcher { name: "c" }; let d4 = MockDispatcher { name: "d" }; let d5 = MockDispatcher { name: "e" }; let d6 = MockDispatcher { name: "f" }; let d7 = MockDispatcher { name: "g" }; let dispatchers: Dispatchers = Dispatchers::from((d1, d2, d3, d4, d5, d6, d7)); assert_eq!(dispatchers.dispatcher.len(), 7); } #[test] fn test_dispatchers_from_vec_of_boxed() { let d1: Box + Send + Sync> = Box::new(MockDispatcher { name: "a" }); let d2: Box + Send + Sync> = Box::new(MockDispatcher { name: "b" }); let dispatchers: Dispatchers = vec![d1, d2].into(); assert_eq!(dispatchers.dispatcher.len(), 2); } #[test] fn test_dispatchers_from_single_boxed() { let d: Box + Send + Sync> = Box::new(MockDispatcher { name: "x" }); let dispatchers: Dispatchers = d.into(); assert_eq!(dispatchers.dispatcher.len(), 1); } #[test] fn test_dispatchers_deref() { let disp = MockDispatcher { name: "test" }; let dispatchers: Dispatchers = Dispatchers::from((disp,)); let inner: &Vec + Send + Sync + 'static>> = &dispatchers; assert_eq!(inner.len(), 1); } #[test] fn test_dispatchers_into_vec() { let disp = MockDispatcher { name: "foo" }; let dispatchers: Dispatchers = Dispatchers::from((disp,)); let vec: Vec + Send + Sync + 'static>> = dispatchers.into(); assert_eq!(vec.len(), 1); } #[test] fn test_box_clone_dispatcher() { let disp: Box> = Box::new(MockDispatcher { name: "clonable" }); let cloned = disp.clone_dispatcher(); assert_eq!(cloned.node().to_string(), "clonable"); } }