use std::collections::HashMap;
use serde_json::Value;
pub type ModuleResult = Result<Value, ModuleError>;
#[derive(Debug, Clone)]
pub struct ModuleError {
pub code: String,
pub message: String,
}
impl std::fmt::Display for ModuleError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
impl std::error::Error for ModuleError {}
pub trait RyditModule: Send + Sync {
fn name(&self) -> &'static str;
fn version(&self) -> &'static str;
fn register(&self) -> HashMap<&'static str, &'static str>;
fn execute(&self, command: &str, params: Value) -> ModuleResult;
}
#[derive(Default)]
pub struct ModuleRegistry {
modules: HashMap<String, Box<dyn RyditModule>>,
}
impl ModuleRegistry {
pub fn new() -> Self {
Self {
modules: HashMap::new(),
}
}
pub fn register<M: RyditModule + 'static>(&mut self, module: M) {
let name = module.name().to_string();
self.modules.insert(name, Box::new(module));
}
pub fn get(&self, name: &str) -> Option<&dyn RyditModule> {
self.modules.get(name).map(|b| b.as_ref())
}
pub fn list(&self) -> Vec<&str> {
self.modules.keys().map(|s| s.as_str()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestModule;
impl RyditModule for TestModule {
fn name(&self) -> &'static str {
"test"
}
fn version(&self) -> &'static str {
"1.0.0"
}
fn register(&self) -> HashMap<&'static str, &'static str> {
let mut cmds = HashMap::new();
cmds.insert("ping", "Test ping command");
cmds.insert("echo", "Test echo command");
cmds
}
fn execute(&self, command: &str, params: Value) -> ModuleResult {
match command {
"ping" => Ok(Value::String("pong".to_string())),
"echo" => Ok(params),
_ => Err(ModuleError {
code: "UNKNOWN_COMMAND".to_string(),
message: format!("Unknown command: {}", command),
}),
}
}
}
#[test]
fn test_module_registry() {
let mut registry = ModuleRegistry::new();
registry.register(TestModule);
assert_eq!(registry.list().len(), 1);
assert!(registry.get("test").is_some());
assert!(registry.get("unknown").is_none());
}
#[test]
fn test_module_execute_ping() {
let mut registry = ModuleRegistry::new();
registry.register(TestModule);
let module = registry.get("test").unwrap();
let result = module.execute("ping", Value::Null).unwrap();
assert_eq!(result, Value::String("pong".to_string()));
}
#[test]
fn test_module_execute_echo() {
let mut registry = ModuleRegistry::new();
registry.register(TestModule);
let module = registry.get("test").unwrap();
let input = Value::String("hello".to_string());
let result = module.execute("echo", input.clone()).unwrap();
assert_eq!(result, input);
}
#[test]
fn test_module_error() {
let mut registry = ModuleRegistry::new();
registry.register(TestModule);
let module = registry.get("test").unwrap();
let result = module.execute("unknown", Value::Null);
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.code, "UNKNOWN_COMMAND");
}
}