use crate::executor::CommandHandler;
pub mod system;
#[cfg(feature = "wasm-plugins")]
pub mod wasm;
pub use system::SystemPlugin;
pub trait Plugin: Send + Sync {
fn name(&self) -> &str;
fn version(&self) -> &str;
fn description(&self) -> &str;
fn handlers(&self) -> Vec<(String, Box<dyn CommandHandler>)>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ExecutionContext;
use crate::Result;
use std::any::Any;
use std::collections::HashMap;
#[derive(Default)]
struct TestContext;
impl ExecutionContext for TestContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
struct EchoPlugin;
impl Plugin for EchoPlugin {
fn name(&self) -> &str {
"echo"
}
fn version(&self) -> &str {
"0.1.0"
}
fn description(&self) -> &str {
"Echoes its arguments"
}
fn handlers(&self) -> Vec<(String, Box<dyn CommandHandler>)> {
struct EchoHandler;
impl CommandHandler for EchoHandler {
fn execute(
&self,
_ctx: &mut dyn ExecutionContext,
args: &HashMap<String, String>,
) -> Result<()> {
for (k, v) in args {
println!("{k}={v}");
}
Ok(())
}
}
vec![("echo_handler".to_string(), Box::new(EchoHandler))]
}
}
struct MultiHandlerPlugin;
impl Plugin for MultiHandlerPlugin {
fn name(&self) -> &str {
"multi"
}
fn version(&self) -> &str {
"1.0.0"
}
fn description(&self) -> &str {
"Plugin with multiple handlers"
}
fn handlers(&self) -> Vec<(String, Box<dyn CommandHandler>)> {
struct NoopHandler;
impl CommandHandler for NoopHandler {
fn execute(
&self,
_: &mut dyn ExecutionContext,
_: &HashMap<String, String>,
) -> Result<()> {
Ok(())
}
}
vec![
("multi_alpha".to_string(), Box::new(NoopHandler)),
("multi_beta".to_string(), Box::new(NoopHandler)),
("multi_gamma".to_string(), Box::new(NoopHandler)),
]
}
}
struct MetadataPlugin;
impl Plugin for MetadataPlugin {
fn name(&self) -> &str {
"acme-analytics"
}
fn version(&self) -> &str {
"3.1.4"
}
fn description(&self) -> &str {
"Analytics commands for Acme Corp"
}
fn handlers(&self) -> Vec<(String, Box<dyn CommandHandler>)> {
vec![]
}
}
#[test]
fn test_plugin_is_object_safe() {
let _: Box<dyn Plugin> = Box::new(EchoPlugin);
}
#[test]
fn test_plugin_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<EchoPlugin>();
assert_send_sync::<MultiHandlerPlugin>();
assert_send_sync::<SystemPlugin>();
}
#[test]
fn test_echo_plugin_metadata() {
let p = EchoPlugin;
assert_eq!(p.name(), "echo");
assert_eq!(p.version(), "0.1.0");
assert_eq!(p.description(), "Echoes its arguments");
}
#[test]
fn test_echo_plugin_handlers_count() {
let handlers = EchoPlugin.handlers();
assert_eq!(handlers.len(), 1);
assert_eq!(handlers[0].0, "echo_handler");
}
#[test]
fn test_echo_handler_executes() {
let handlers = EchoPlugin.handlers();
let (_, handler) = &handlers[0];
let mut ctx = TestContext;
let mut args = HashMap::new();
args.insert("key".to_string(), "value".to_string());
assert!(handler.execute(&mut ctx, &args).is_ok());
}
#[test]
fn test_multi_handler_plugin_count() {
let handlers = MultiHandlerPlugin.handlers();
assert_eq!(handlers.len(), 3);
let names: Vec<&str> = handlers.iter().map(|(n, _)| n.as_str()).collect();
assert!(names.contains(&"multi_alpha"));
assert!(names.contains(&"multi_beta"));
assert!(names.contains(&"multi_gamma"));
}
#[test]
fn test_metadata_plugin_fields() {
let p = MetadataPlugin;
assert_eq!(p.name(), "acme-analytics");
assert_eq!(p.version(), "3.1.4");
assert_eq!(p.description(), "Analytics commands for Acme Corp");
assert_eq!(p.handlers().len(), 0);
}
#[test]
fn test_plugin_trait_object_in_vec() {
let plugins: Vec<Box<dyn Plugin>> = vec![
Box::new(EchoPlugin),
Box::new(MultiHandlerPlugin),
Box::new(MetadataPlugin),
Box::new(SystemPlugin::new()),
];
assert_eq!(plugins.len(), 4);
let names: Vec<&str> = plugins.iter().map(|p| p.name()).collect();
assert!(names.contains(&"echo"));
assert!(names.contains(&"multi"));
assert!(names.contains(&"acme-analytics"));
assert!(names.contains(&"system"));
}
#[test]
fn test_plugin_handlers_total_count() {
let plugins: Vec<Box<dyn Plugin>> =
vec![Box::new(EchoPlugin), Box::new(MultiHandlerPlugin)];
let total: usize = plugins.iter().map(|p| p.handlers().len()).sum();
assert_eq!(total, 4); }
}