use async_trait::async_trait;
use std::collections::HashMap;
pub mod attributes;
pub mod context;
pub mod handlers;
pub mod registry;
pub mod result;
pub use attributes::{AttributeSchema, AttributeValue};
pub use context::ExecutionContext;
pub use registry::CommandRegistry;
pub use result::{CommandError, CommandResult};
#[async_trait]
pub trait CommandHandler: Send + Sync {
fn name(&self) -> &str;
fn schema(&self) -> AttributeSchema;
fn validate(&self, attributes: &HashMap<String, AttributeValue>) -> Result<(), CommandError> {
self.schema().validate(attributes)
}
async fn execute(
&self,
context: &ExecutionContext,
attributes: HashMap<String, AttributeValue>,
) -> CommandResult;
fn description(&self) -> &str;
fn examples(&self) -> Vec<String> {
vec![]
}
}
pub struct CommandHandlerBuilder {
registry: CommandRegistry,
}
impl CommandHandlerBuilder {
pub fn new() -> Self {
Self {
registry: CommandRegistry::new(),
}
}
pub fn register(self, handler: Box<dyn CommandHandler>) -> Self {
self.registry.register(handler);
self
}
pub fn build(self) -> CommandRegistry {
self.registry
}
}
impl Default for CommandHandlerBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
struct MockHandler {
name: String,
schema: AttributeSchema,
}
#[async_trait]
impl CommandHandler for MockHandler {
fn name(&self) -> &str {
&self.name
}
fn schema(&self) -> AttributeSchema {
self.schema.clone()
}
async fn execute(
&self,
_context: &ExecutionContext,
_attributes: HashMap<String, AttributeValue>,
) -> CommandResult {
CommandResult::success(Value::String("Mock executed".to_string()))
}
fn description(&self) -> &str {
"Mock command handler for testing"
}
}
#[tokio::test]
async fn test_command_handler_builder() {
let handler = Box::new(MockHandler {
name: "mock".to_string(),
schema: AttributeSchema::new("mock"),
});
let registry = CommandHandlerBuilder::new().register(handler).build();
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
assert!(registry.get("mock").await.is_some());
}
#[tokio::test]
async fn test_handler_validation() {
let mut schema = AttributeSchema::new("test");
schema.add_required("command", "The command to run");
let handler = MockHandler {
name: "test".to_string(),
schema,
};
let mut attrs = HashMap::new();
attrs.insert(
"command".to_string(),
AttributeValue::String("echo test".to_string()),
);
assert!(handler.validate(&attrs).is_ok());
let empty_attrs = HashMap::new();
assert!(handler.validate(&empty_attrs).is_err());
}
}