use serde::{Deserialize, Serialize};
use crate::error::EngineError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action {
pub name: String,
pub payload: Option<serde_json::Value>,
pub sender: Option<String>,
}
impl Action {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
payload: None,
sender: None,
}
}
pub fn with_payload(mut self, payload: serde_json::Value) -> Self {
self.payload = Some(payload);
self
}
pub fn with_sender(mut self, sender: impl Into<String>) -> Self {
self.sender = Some(sender.into());
self
}
}
pub type ActionHandler = Box<dyn Fn(&Action) + Send + Sync>;
pub struct ActionDispatcher {
handlers: indexmap::IndexMap<String, ActionHandler>,
}
impl ActionDispatcher {
pub fn new() -> Self {
Self {
handlers: indexmap::IndexMap::new(),
}
}
pub fn on<F>(&mut self, action_name: impl Into<String>, handler: F)
where
F: Fn(&Action) + Send + Sync + 'static,
{
self.handlers.insert(action_name.into(), Box::new(handler));
}
pub fn dispatch(&self, action: &Action) -> Result<(), EngineError> {
if let Some(handler) = self.handlers.get(&action.name) {
handler(action);
Ok(())
} else {
Err(EngineError::ActionNotFound(action.name.clone()))
}
}
pub fn has_handler(&self, action_name: &str) -> bool {
self.handlers.contains_key(action_name)
}
pub fn remove(&mut self, action_name: &str) {
self.handlers.shift_remove(action_name);
}
pub fn clear(&mut self) {
self.handlers.clear();
}
}
impl Default for ActionDispatcher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[test]
fn test_action_dispatch() {
let mut dispatcher = ActionDispatcher::new();
let called = Arc::new(Mutex::new(false));
let called_clone = called.clone();
dispatcher.on("test", move |_action| {
*called_clone.lock().unwrap() = true;
});
let action = Action::new("test");
dispatcher.dispatch(&action).unwrap();
assert!(*called.lock().unwrap());
}
#[test]
fn test_missing_handler() {
let dispatcher = ActionDispatcher::new();
let action = Action::new("unknown");
assert!(dispatcher.dispatch(&action).is_err());
}
}