1use serde_json::Value;
9use std::collections::HashMap;
10
11pub type ModuleResult = Result<Value, ModuleError>;
13
14#[derive(Debug, Clone)]
16pub struct ModuleError {
17 pub code: String,
18 pub message: String,
19}
20
21impl std::fmt::Display for ModuleError {
22 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23 write!(f, "[{}] {}", self.code, self.message)
24 }
25}
26
27impl std::error::Error for ModuleError {}
28
29#[derive(Debug, Clone, Default)]
33pub struct ModuleMetadata {
34 pub name: &'static str,
36 pub version: &'static str,
38 pub authors: Vec<&'static str>,
40 pub description: &'static str,
42 pub license: &'static str,
44 pub dependencies: Vec<&'static str>,
46}
47
48impl ModuleMetadata {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn with_name(mut self, name: &'static str) -> Self {
56 self.name = name;
57 self
58 }
59
60 pub fn with_version(mut self, version: &'static str) -> Self {
62 self.version = version;
63 self
64 }
65
66 pub fn with_authors(mut self, authors: Vec<&'static str>) -> Self {
68 self.authors = authors;
69 self
70 }
71
72 pub fn with_description(mut self, description: &'static str) -> Self {
74 self.description = description;
75 self
76 }
77
78 pub fn with_license(mut self, license: &'static str) -> Self {
80 self.license = license;
81 self
82 }
83
84 pub fn with_dependencies(mut self, deps: Vec<&'static str>) -> Self {
86 self.dependencies = deps;
87 self
88 }
89}
90
91pub trait RyditModule: Send + Sync {
121 fn name(&self) -> &'static str;
123
124 fn version(&self) -> &'static str;
126
127 fn register(&self) -> HashMap<&'static str, &'static str>;
130
131 fn execute(&self, command: &str, params: Value) -> ModuleResult;
137
138 fn metadata(&self) -> ModuleMetadata {
143 ModuleMetadata {
144 name: self.name(),
145 version: self.version(),
146 authors: vec![],
147 description: "",
148 license: "MIT",
149 dependencies: vec![],
150 }
151 }
152
153 fn on_reload(&mut self) {}
158
159 fn on_unload(&mut self) {}
164}
165
166impl RyditModule for Box<dyn RyditModule> {
170 fn name(&self) -> &'static str {
171 self.as_ref().name()
172 }
173 fn version(&self) -> &'static str {
174 self.as_ref().version()
175 }
176 fn register(&self) -> HashMap<&'static str, &'static str> {
177 self.as_ref().register()
178 }
179 fn execute(&self, command: &str, params: Value) -> ModuleResult {
180 self.as_ref().execute(command, params)
181 }
182 fn metadata(&self) -> ModuleMetadata {
183 self.as_ref().metadata()
184 }
185 fn on_reload(&mut self) {
186 self.as_mut().on_reload()
187 }
188 fn on_unload(&mut self) {
189 self.as_mut().on_unload()
190 }
191}
192
193#[derive(Default)]
197pub struct ModuleRegistry {
198 modules: HashMap<String, Box<dyn RyditModule>>,
199}
200
201impl ModuleRegistry {
202 pub fn new() -> Self {
204 Self {
205 modules: HashMap::new(),
206 }
207 }
208
209 pub fn register<M: RyditModule + 'static>(&mut self, module: M) {
211 let name = module.name().to_string();
212 self.modules.insert(name, Box::new(module));
213 }
214
215 pub fn get(&self, name: &str) -> Option<&dyn RyditModule> {
217 self.modules.get(name).map(|b| b.as_ref())
218 }
219
220 pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn RyditModule>> {
222 self.modules.get_mut(name)
223 }
224
225 pub fn list(&self) -> Vec<&str> {
227 self.modules.keys().map(|s| s.as_str()).collect()
228 }
229
230 pub fn list_with_metadata(&self) -> Vec<(&str, ModuleMetadata)> {
232 self.modules
233 .values()
234 .map(|m| (m.name(), m.metadata()))
235 .collect()
236 }
237
238 pub fn reload(&mut self, name: &str) {
242 if let Some(module) = self.modules.get_mut(name) {
243 module.on_reload();
244 }
245 }
246
247 pub fn unload(&mut self, name: &str) {
251 if let Some(mut module) = self.modules.remove(name) {
252 module.on_unload();
253 }
254 }
255
256 pub fn contains(&self, name: &str) -> bool {
258 self.modules.contains_key(name)
259 }
260
261 pub fn len(&self) -> usize {
263 self.modules.len()
264 }
265
266 pub fn is_empty(&self) -> bool {
268 self.modules.is_empty()
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 struct TestModule;
278
279 impl RyditModule for TestModule {
280 fn name(&self) -> &'static str {
281 "test"
282 }
283
284 fn version(&self) -> &'static str {
285 "1.0.0"
286 }
287
288 fn register(&self) -> HashMap<&'static str, &'static str> {
289 let mut cmds = HashMap::new();
290 cmds.insert("ping", "Test ping command");
291 cmds.insert("echo", "Test echo command");
292 cmds
293 }
294
295 fn execute(&self, command: &str, params: Value) -> ModuleResult {
296 match command {
297 "ping" => Ok(Value::String("pong".to_string())),
298 "echo" => Ok(params),
299 _ => Err(ModuleError {
300 code: "UNKNOWN_COMMAND".to_string(),
301 message: format!("Unknown command: {}", command),
302 }),
303 }
304 }
305
306 fn metadata(&self) -> ModuleMetadata {
307 ModuleMetadata::new()
308 .with_name("test")
309 .with_version("1.0.0")
310 .with_description("Módulo de prueba para tests")
311 .with_license("MIT")
312 }
313 }
314
315 #[test]
316 fn test_module_registry() {
317 let mut registry = ModuleRegistry::new();
318 registry.register(TestModule);
319
320 assert_eq!(registry.list().len(), 1);
321 assert!(registry.get("test").is_some());
322 assert!(registry.get("unknown").is_none());
323 }
324
325 #[test]
326 fn test_module_execute_ping() {
327 let mut registry = ModuleRegistry::new();
328 registry.register(TestModule);
329
330 let module = registry.get("test").unwrap();
331 let result = module.execute("ping", Value::Null).unwrap();
332 assert_eq!(result, Value::String("pong".to_string()));
333 }
334
335 #[test]
336 fn test_module_execute_echo() {
337 let mut registry = ModuleRegistry::new();
338 registry.register(TestModule);
339
340 let module = registry.get("test").unwrap();
341 let input = Value::String("hello".to_string());
342 let result = module.execute("echo", input.clone()).unwrap();
343 assert_eq!(result, input);
344 }
345
346 #[test]
347 fn test_module_error() {
348 let mut registry = ModuleRegistry::new();
349 registry.register(TestModule);
350
351 let module = registry.get("test").unwrap();
352 let result = module.execute("unknown", Value::Null);
353 assert!(result.is_err());
354
355 let err = result.unwrap_err();
356 assert_eq!(err.code, "UNKNOWN_COMMAND");
357 }
358
359 #[test]
360 fn test_module_metadata() {
361 let mut registry = ModuleRegistry::new();
362 registry.register(TestModule);
363
364 let module = registry.get("test").unwrap();
365 let metadata = module.metadata();
366
367 assert_eq!(metadata.name, "test");
368 assert_eq!(metadata.version, "1.0.0");
369 assert_eq!(metadata.description, "Módulo de prueba para tests");
370 assert_eq!(metadata.license, "MIT");
371 }
372
373 #[test]
374 fn test_module_registry_with_metadata() {
375 let mut registry = ModuleRegistry::new();
376 registry.register(TestModule);
377
378 let list = registry.list_with_metadata();
379 assert_eq!(list.len(), 1);
380
381 let (name, metadata) = &list[0];
382 assert_eq!(*name, "test");
383 assert_eq!(metadata.name, "test");
384 }
385
386 #[test]
387 fn test_module_reload() {
388 let mut registry = ModuleRegistry::new();
389 registry.register(TestModule);
390
391 registry.reload("test");
393 assert!(registry.contains("test"));
394
395 registry.reload("nonexistent");
397 assert!(!registry.contains("nonexistent"));
398 }
399
400 #[test]
401 fn test_module_unload() {
402 let mut registry = ModuleRegistry::new();
403 registry.register(TestModule);
404
405 assert!(registry.contains("test"));
406
407 registry.unload("test");
409 assert!(!registry.contains("test"));
410
411 registry.unload("nonexistent");
413 assert!(!registry.contains("nonexistent"));
414 }
415
416 #[test]
417 fn test_module_registry_len() {
418 let mut registry = ModuleRegistry::new();
419 assert!(registry.is_empty());
420
421 registry.register(TestModule);
422 assert_eq!(registry.len(), 1);
423 assert!(!registry.is_empty());
424
425 registry.unload("test");
426 assert!(registry.is_empty());
427 }
428}