hypen_engine/dispatch/
action.rs1use serde::{Deserialize, Serialize};
2
3use crate::error::EngineError;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Action {
8 pub name: String,
10
11 pub payload: Option<serde_json::Value>,
13
14 pub sender: Option<String>,
16}
17
18impl Action {
19 pub fn new(name: impl Into<String>) -> Self {
20 Self {
21 name: name.into(),
22 payload: None,
23 sender: None,
24 }
25 }
26
27 pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
28 self.payload = Some(payload);
29 self
30 }
31
32 pub fn with_sender(mut self, sender: impl Into<String>) -> Self {
33 self.sender = Some(sender.into());
34 self
35 }
36}
37
38pub type ActionHandler = Box<dyn Fn(&Action) + Send + Sync>;
40
41pub struct ActionDispatcher {
43 handlers: indexmap::IndexMap<String, ActionHandler>,
45}
46
47impl ActionDispatcher {
48 pub fn new() -> Self {
49 Self {
50 handlers: indexmap::IndexMap::new(),
51 }
52 }
53
54 pub fn on<F>(&mut self, action_name: impl Into<String>, handler: F)
56 where
57 F: Fn(&Action) + Send + Sync + 'static,
58 {
59 self.handlers.insert(action_name.into(), Box::new(handler));
60 }
61
62 pub fn dispatch(&self, action: &Action) -> Result<(), EngineError> {
64 if let Some(handler) = self.handlers.get(&action.name) {
65 handler(action);
66 Ok(())
67 } else {
68 Err(EngineError::ActionNotFound(action.name.clone()))
69 }
70 }
71
72 pub fn has_handler(&self, action_name: &str) -> bool {
74 self.handlers.contains_key(action_name)
75 }
76
77 pub fn remove(&mut self, action_name: &str) {
79 self.handlers.shift_remove(action_name);
80 }
81
82 pub fn clear(&mut self) {
84 self.handlers.clear();
85 }
86}
87
88impl Default for ActionDispatcher {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use std::sync::{Arc, Mutex};
98
99 #[test]
100 fn test_action_dispatch() {
101 let mut dispatcher = ActionDispatcher::new();
102 let called = Arc::new(Mutex::new(false));
103 let called_clone = called.clone();
104
105 dispatcher.on("test", move |_action| {
106 *called_clone.lock().unwrap() = true;
107 });
108
109 let action = Action::new("test");
110 dispatcher.dispatch(&action).unwrap();
111
112 assert!(*called.lock().unwrap());
113 }
114
115 #[test]
116 fn test_missing_handler() {
117 let dispatcher = ActionDispatcher::new();
118 let action = Action::new("unknown");
119 assert!(dispatcher.dispatch(&action).is_err());
120 }
121}