aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/asset
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-17 03:27:46 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-17 03:27:46 +0800
commitc23c590330af83afb6e146bcd9b0a274b3689d22 (patch)
tree34599cf2737ce4de22653c9ccae2d60a38a15842 /mingling_core/src/asset
parent6980fbf2f9fb4c599d8dc6ff549a8b3288eb24e9 (diff)
refactor!: remove Node type and simplify dispatcher macro syntax
The `dispatcher!` macro no longer requires a `CMD*` dispatcher type argument; the dispatcher struct is now generated internally as `__Dispatcher{Pascal}`. The `Node` type, `node!` macro, and `Dispatcher::node()` / `clone_dispatcher()` methods are removed.
Diffstat (limited to 'mingling_core/src/asset')
-rw-r--r--mingling_core/src/asset/dispatcher.rs141
-rw-r--r--mingling_core/src/asset/node.rs157
2 files changed, 3 insertions, 295 deletions
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index 1ab7bf6..79ebee0 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -1,6 +1,4 @@
-use std::fmt::Display;
-
-use crate::{ChainProcess, asset::node::Node};
+use crate::ChainProcess;
/// The entry logic of the Mingling program
///
@@ -14,7 +12,6 @@ use crate::{ChainProcess, asset::node::Node};
/// # use mingling_core::Dispatcher;
/// # use mingling_core::Grouped;
/// # use mingling_core::Routable;
-/// # use mingling_core::Node;
/// # use mingling_core::MockProgramCollect as ThisProgram;
/// # unsafe impl Grouped<ThisProgram> for Foo {
/// # fn member_id() -> ThisProgram { ThisProgram::Foo }
@@ -25,54 +22,14 @@ use crate::{ChainProcess, asset::node::Node};
/// }
///
/// impl Dispatcher<ThisProgram> for CMDGreet {
-/// fn node(&self) -> Node {
-/// Node::default().join("greet")
-/// }
-///
/// fn begin(&self, args: Vec<String>) -> ChainProcess<ThisProgram> {
/// Routable::to_chain(Foo { args })
/// }
-///
-/// fn clone_dispatcher(&self) -> Box<dyn Dispatcher<ThisProgram>> {
-/// Box::new(CMDGreet)
-/// }
/// }
/// ```
pub trait Dispatcher<C> {
- /// Get the node of this Dispatcher, used to tell the program loop which arguments should be handled by this Dispatcher
- ///
- /// Example:
- ///
- /// ```
- /// # use mingling_core::ChainProcess;
- /// # use mingling_core::Dispatcher;
- /// # use mingling_core::Grouped;
- /// # use mingling_core::Routable;
- /// # use mingling_core::Node;
- /// # use mingling_core::MockProgramCollect as ThisProgram;
- /// # unsafe impl Grouped<ThisProgram> for Foo {
- /// # fn member_id() -> ThisProgram { ThisProgram::Foo }
- /// # }
- /// # struct CMDGreet;
- /// # struct Foo {
- /// # args: Vec<String>
- /// # }
- /// # impl Dispatcher<ThisProgram> for CMDGreet {
- /// fn node(&self) -> Node {
- /// // Construct the node
- /// Node::default().join("greet")
- /// }
- /// # fn begin(&self, args: Vec<String>) -> ChainProcess<ThisProgram> {
- /// # Routable::to_chain(Foo { args })
- /// # }
- /// # fn clone_dispatcher(&self) -> Box<dyn Dispatcher<ThisProgram>> {
- /// # Box::new(CMDGreet)
- /// # }
- /// # }
- /// ```
- fn node(&self) -> Node;
-
- /// Begin logic, receives the remaining arguments after the prefix has been stripped
+ /// Begin logic, receives the remaining arguments after the command prefix
+ /// has been stripped
///
/// Example:
///
@@ -81,7 +38,6 @@ pub trait Dispatcher<C> {
/// # use mingling_core::Dispatcher;
/// # use mingling_core::Grouped;
/// # use mingling_core::Routable;
- /// # use mingling_core::Node;
/// # use mingling_core::MockProgramCollect as ThisProgram;
/// # unsafe impl Grouped<ThisProgram> for Foo {
/// # fn member_id() -> ThisProgram { ThisProgram::Foo }
@@ -91,102 +47,11 @@ pub trait Dispatcher<C> {
/// # args: Vec<String>
/// # }
/// # impl Dispatcher<ThisProgram> for CMDGreet {
- /// # fn node(&self) -> Node {
- /// # Node::default().join("greet")
- /// # }
/// fn begin(&self, args: Vec<String>) -> ChainProcess<ThisProgram> {
/// // Create Foo from args and route it to the next chain
/// Routable::to_chain(Foo { args })
/// }
- /// # fn clone_dispatcher(&self) -> Box<dyn Dispatcher<ThisProgram>> {
- /// # Box::new(CMDGreet)
- /// # }
/// # }
/// ```
fn begin(&self, args: Vec<String>) -> ChainProcess<C>;
-
- /// Clone the dispatcher's Box for dynamic dispatch
- ///
- /// Example:
- ///
- /// ```
- /// # use mingling_core::ChainProcess;
- /// # use mingling_core::Dispatcher;
- /// # use mingling_core::Grouped;
- /// # use mingling_core::Routable;
- /// # use mingling_core::Node;
- /// # use mingling_core::MockProgramCollect as ThisProgram;
- /// # unsafe impl Grouped<ThisProgram> for Foo {
- /// # fn member_id() -> ThisProgram { ThisProgram::Foo }
- /// # }
- /// # struct CMDGreet;
- /// # struct Foo {
- /// # args: Vec<String>
- /// # }
- /// # impl Dispatcher<ThisProgram> for CMDGreet {
- /// # fn node(&self) -> Node {
- /// # Node::default().join("greet")
- /// # }
- /// # fn begin(&self, args: Vec<String>) -> ChainProcess<ThisProgram> {
- /// # Routable::to_chain(Foo { args })
- /// # }
- /// fn clone_dispatcher(&self) -> Box<dyn Dispatcher<ThisProgram>> {
- /// // Create a new Box
- /// Box::new(CMDGreet)
- /// }
- /// # }
- /// ```
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher<C>>;
-}
-
-impl<G> Clone for Box<dyn Dispatcher<G>>
-where
- G: Display,
-{
- fn clone(&self) -> Self {
- self.clone_dispatcher()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::ChainProcess;
- use std::fmt::Display;
- #[derive(Clone)]
- struct MockDispatcher {
- name: &'static str,
- }
-
- impl<C: Display> Dispatcher<C> for MockDispatcher {
- fn node(&self) -> crate::asset::node::Node {
- self.name.into()
- }
-
- fn begin(&self, _args: Vec<String>) -> ChainProcess<C> {
- unimplemented!("not used in these tests")
- }
-
- fn clone_dispatcher(&self) -> Box<dyn Dispatcher<C>> {
- Box::new(self.clone())
- }
- }
- #[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_box_clone_dispatcher() {
- let disp: Box<dyn Dispatcher<MockG>> = Box::new(MockDispatcher { name: "clonable" });
- let cloned = disp.clone_dispatcher();
- assert_eq!(cloned.node().to_string(), "clonable");
- }
}
diff --git a/mingling_core/src/asset/node.rs b/mingling_core/src/asset/node.rs
deleted file mode 100644
index ee28836..0000000
--- a/mingling_core/src/asset/node.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-use just_fmt::kebab_case;
-
-/// Represents a command node used for matching user-input command paths.
-///
-/// The node consists of multiple segments, separated by dots (`.`), which are automatically
-/// converted to kebab-case. For example, the input string `"node.subnode"` would be converted
-/// to the node representation `["node", "subnode"]`.
-///
-/// # Examples
-///
-/// ```
-/// use mingling_core::Node;
-///
-/// // Create a node and append child segments
-/// let node = Node::from("base").join("sub").join("leaf");
-/// assert_eq!(node.to_string(), "base.sub.leaf");
-/// ```
-#[derive(Debug, Default)]
-pub struct Node {
- node: Vec<String>,
-}
-
-impl Node {
- /// Appends a new segment to the node path.
- ///
- /// This method consumes the current node and returns a new node with the new
- /// segment appended to the end of the path.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_core::Node;
- ///
- /// let node = Node::from("base").join("sub");
- /// assert_eq!(node.to_string(), "base.sub");
- /// ```
- #[must_use]
- pub fn join(self, node: impl Into<String>) -> Self {
- let mut new_node = self.node;
- new_node.push(node.into());
- Self { node: new_node }
- }
-}
-
-impl From<&str> for Node {
- fn from(s: &str) -> Self {
- let node = s.split('.').map(|part| kebab_case!(part)).collect();
- Self { node }
- }
-}
-
-impl From<String> for Node {
- fn from(s: String) -> Self {
- let node = s.split('.').map(|part| kebab_case!(part)).collect();
- Self { node }
- }
-}
-
-impl PartialEq for Node {
- fn eq(&self, other: &Self) -> bool {
- self.node == other.node
- }
-}
-
-impl Eq for Node {}
-
-impl PartialOrd for Node {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Node {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.node.cmp(&other.node)
- }
-}
-
-impl std::fmt::Display for Node {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.node.join("."))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_node_from_single_str() {
- let node = Node::from("hello");
- assert_eq!(node.node, vec!["hello"]);
- assert_eq!(node.to_string(), "hello");
- }
-
- #[test]
- fn test_node_from_dotted_str() {
- let node = Node::from("a.b.c");
- assert_eq!(node.node, vec!["a", "b", "c"]);
- assert_eq!(node.to_string(), "a.b.c");
- }
-
- #[test]
- fn test_node_kebab_case_conversion() {
- let node = Node::from("HelloWorld.FooBar");
- assert_eq!(node.node, vec!["hello-world", "foo-bar"]);
- }
-
- #[test]
- fn test_node_from_string() {
- let s = String::from("x.y");
- let node = Node::from(s);
- assert_eq!(node.node, vec!["x", "y"]);
- }
-
- #[test]
- fn test_node_join() {
- let node = Node::from("base").join("sub");
- assert_eq!(node.node, vec!["base", "sub"]);
- }
-
- #[test]
- fn test_node_join_multiple() {
- let node = Node::from("a").join("b").join("c");
- assert_eq!(node.to_string(), "a.b.c");
- }
-
- #[test]
- fn test_node_default_empty() {
- let node = Node::default();
- assert!(node.node.is_empty());
- assert_eq!(node.to_string(), "");
- }
-
- #[test]
- fn test_node_partial_eq() {
- let a = Node::from("a.b");
- let b = Node::from("a.b");
- let c = Node::from("a.c");
- assert_eq!(a, b);
- assert_ne!(a, c);
- }
-
- #[test]
- fn test_node_ord() {
- let a = Node::from("a");
- let b = Node::from("b");
- assert!(a < b);
- }
-
- #[test]
- fn test_node_join_appends_part() {
- let node = Node::from("existing");
- let joined = node.join("new-part");
- assert_eq!(joined.to_string(), "existing.new-part");
- }
-}