hypen_engine/dispatch/
action.rs1use serde::{Deserialize, Serialize};
2
3use crate::error::EngineError;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Action {
24 pub name: String,
26
27 pub payload: Option<serde_json::Value>,
29
30 pub sender: Option<String>,
32}
33
34impl Action {
35 pub fn new(name: impl Into<String>) -> Self {
37 Self {
38 name: name.into(),
39 payload: None,
40 sender: None,
41 }
42 }
43
44 pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
45 self.payload = Some(payload);
46 self
47 }
48
49 pub fn with_sender(mut self, sender: impl Into<String>) -> Self {
50 self.sender = Some(sender.into());
51 self
52 }
53}
54
55pub type ActionHandler = Box<dyn Fn(&Action) + Send + Sync>;
59
60pub struct ActionDispatcher {
66 handlers: indexmap::IndexMap<String, ActionHandler>,
68}
69
70impl ActionDispatcher {
71 pub fn new() -> Self {
72 Self {
73 handlers: indexmap::IndexMap::new(),
74 }
75 }
76
77 pub fn on<F>(&mut self, action_name: impl Into<String>, handler: F)
79 where
80 F: Fn(&Action) + Send + Sync + 'static,
81 {
82 self.handlers.insert(action_name.into(), Box::new(handler));
83 }
84
85 pub fn dispatch(&self, action: &Action) -> Result<(), EngineError> {
87 if let Some(handler) = self.handlers.get(&action.name) {
88 handler(action);
89 Ok(())
90 } else {
91 Err(EngineError::ActionNotFound(action.name.clone()))
92 }
93 }
94
95 pub fn has_handler(&self, action_name: &str) -> bool {
97 self.handlers.contains_key(action_name)
98 }
99
100 pub fn remove(&mut self, action_name: &str) {
102 self.handlers.shift_remove(action_name);
103 }
104
105 pub fn clear(&mut self) {
107 self.handlers.clear();
108 }
109}
110
111impl Default for ActionDispatcher {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use std::sync::{Arc, Mutex};
121
122 #[test]
123 fn test_action_dispatch() {
124 let mut dispatcher = ActionDispatcher::new();
125 let called = Arc::new(Mutex::new(false));
126 let called_clone = called.clone();
127
128 dispatcher.on("test", move |_action| {
129 *called_clone.lock().unwrap() = true;
130 });
131
132 let action = Action::new("test");
133 dispatcher.dispatch(&action).unwrap();
134
135 assert!(*called.lock().unwrap());
136 }
137
138 #[test]
139 fn test_missing_handler() {
140 let dispatcher = ActionDispatcher::new();
141 let action = Action::new("unknown");
142 assert!(dispatcher.dispatch(&action).is_err());
143 }
144}