use crate::context::ExecutionContext;
use crate::error::Result;
use crate::parser::ParsedArgs;
use async_trait::async_trait;
pub trait CommandHandler: Send + Sync {
fn execute(&self, context: &mut dyn ExecutionContext, args: &ParsedArgs) -> Result<()>;
fn validate(&self, _args: &ParsedArgs) -> Result<()> {
Ok(())
}
}
#[async_trait]
pub trait AsyncCommandHandler: Send + Sync {
async fn execute(&self, context: &mut dyn ExecutionContext, args: &ParsedArgs) -> Result<()>;
async fn validate(&self, _args: &ParsedArgs) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::ExecutionError;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
fn scalar_args<const N: usize>(pairs: [(&str, &str); N]) -> ParsedArgs {
let map: HashMap<String, String> = pairs
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
ParsedArgs::from_scalars(map)
}
#[derive(Default)]
struct TestContext {
state: String,
}
impl ExecutionContext for TestContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
struct HelloCommand;
impl CommandHandler for HelloCommand {
fn execute(&self, context: &mut dyn ExecutionContext, args: &ParsedArgs) -> Result<()> {
let ctx = crate::context::downcast_mut::<TestContext>(context).ok_or_else(|| {
ExecutionError::CommandFailed(anyhow::anyhow!("Wrong context type"))
})?;
let name = args.get_scalar("name").unwrap_or("World");
ctx.state = format!("Hello, {}!", name);
Ok(())
}
}
struct ValidatedCommand;
impl CommandHandler for ValidatedCommand {
fn execute(&self, _context: &mut dyn ExecutionContext, _args: &ParsedArgs) -> Result<()> {
Ok(())
}
fn validate(&self, args: &ParsedArgs) -> Result<()> {
if let Some(count) = args.get_scalar("count") {
let count_val: i32 = count.parse().map_err(|_| {
ExecutionError::CommandFailed(anyhow::anyhow!("count must be an integer"))
})?;
if count_val <= 0 {
return Err(ExecutionError::CommandFailed(anyhow::anyhow!(
"count must be positive"
))
.into());
}
} else {
return Err(
ExecutionError::CommandFailed(anyhow::anyhow!("count is required")).into(),
);
}
Ok(())
}
}
struct FailingCommand;
impl CommandHandler for FailingCommand {
fn execute(&self, _context: &mut dyn ExecutionContext, _args: &ParsedArgs) -> Result<()> {
Err(ExecutionError::CommandFailed(anyhow::anyhow!("Simulated failure")).into())
}
}
struct StatefulCommand;
impl CommandHandler for StatefulCommand {
fn execute(&self, context: &mut dyn ExecutionContext, args: &ParsedArgs) -> Result<()> {
let ctx = crate::context::downcast_mut::<TestContext>(context).ok_or_else(|| {
ExecutionError::CommandFailed(anyhow::anyhow!("Wrong context type"))
})?;
let value = args.get_scalar("value").unwrap_or("default");
ctx.state.push_str(value);
Ok(())
}
}
#[test]
fn test_basic_execution() {
let handler = HelloCommand;
let mut context = TestContext::default();
let args = scalar_args([("name", "Rust")]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, Rust!");
}
#[test]
fn test_execution_without_args() {
let handler = HelloCommand;
let mut context = TestContext::default();
let args = ParsedArgs::from_scalars(HashMap::new());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, World!");
}
#[test]
fn test_execution_with_empty_name() {
let handler = HelloCommand;
let mut context = TestContext::default();
let args = scalar_args([("name", "")]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, !");
}
#[test]
fn test_default_validation_accepts_all() {
let handler = HelloCommand;
let args = scalar_args([("random", "value")]);
let result = handler.validate(&args);
assert!(result.is_ok());
}
#[test]
fn test_custom_validation_success() {
let handler = ValidatedCommand;
let args = scalar_args([("count", "5")]);
let result = handler.validate(&args);
assert!(result.is_ok());
}
#[test]
fn test_custom_validation_missing_arg() {
let handler = ValidatedCommand;
let args = ParsedArgs::from_scalars(HashMap::new());
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("required"));
}
#[test]
fn test_custom_validation_invalid_value() {
let handler = ValidatedCommand;
let args = scalar_args([("count", "0")]);
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("positive"));
}
#[test]
fn test_custom_validation_non_integer() {
let handler = ValidatedCommand;
let args = scalar_args([("count", "abc")]);
let result = handler.validate(&args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("integer"));
}
#[test]
fn test_execution_failure() {
let handler = FailingCommand;
let mut context = TestContext::default();
let args = ParsedArgs::from_scalars(HashMap::new());
let result = handler.execute(&mut context, &args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Simulated failure"));
}
#[test]
fn test_context_downcast_failure() {
struct WrongContext;
impl ExecutionContext for WrongContext {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
let handler = HelloCommand;
let mut wrong_context = WrongContext;
let args = ParsedArgs::from_scalars(HashMap::new());
let result = handler.execute(&mut wrong_context, &args);
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Wrong context type"));
}
#[test]
fn test_context_state_modification() {
let handler = StatefulCommand;
let mut context = TestContext {
state: "initial".to_string(),
};
let args = scalar_args([("value", "_modified")]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "initial_modified");
}
#[test]
fn test_multiple_executions_preserve_state() {
let handler = StatefulCommand;
let mut context = TestContext::default();
let args1 = scalar_args([("value", "first")]);
handler.execute(&mut context, &args1).unwrap();
assert_eq!(context.state, "first");
let args2 = scalar_args([("value", "_second")]);
handler.execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "first_second");
}
#[test]
fn test_trait_object_usage() {
let handler: Box<dyn CommandHandler> = Box::new(HelloCommand);
let mut context = TestContext::default();
let args = scalar_args([("name", "TraitObject")]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, TraitObject!");
}
#[test]
fn test_multiple_trait_objects() {
let handlers: Vec<Box<dyn CommandHandler>> =
vec![Box::new(HelloCommand), Box::new(StatefulCommand)];
let mut context = TestContext::default();
let args1 = scalar_args([("name", "First")]);
handlers[0].execute(&mut context, &args1).unwrap();
assert_eq!(context.state, "Hello, First!");
context.state.clear();
let args2 = scalar_args([("value", "Second")]);
handlers[1].execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "Second");
}
#[test]
fn test_send_sync_requirement() {
let handler: Arc<dyn CommandHandler> = Arc::new(HelloCommand);
let handler_clone = handler.clone();
let _ = std::thread::spawn(move || {
let _h = handler_clone;
});
}
#[test]
fn test_concurrent_validation() {
let handler = Arc::new(ValidatedCommand);
let handler_clone = handler.clone();
let handle = std::thread::spawn(move || {
let args = scalar_args([("count", "10")]);
handler_clone.validate(&args)
});
let args = scalar_args([("count", "5")]);
let result1 = handler.validate(&args);
let result2 = handle.join().unwrap();
assert!(result1.is_ok());
assert!(result2.is_ok());
}
#[test]
fn test_empty_args() {
let handler = StatefulCommand;
let mut context = TestContext::default();
let args = ParsedArgs::from_scalars(HashMap::new());
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "default");
}
#[test]
fn test_args_with_special_characters() {
let handler = HelloCommand;
let mut context = TestContext::default();
let args = scalar_args([("name", "Hello, 世界! 🌍")]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert_eq!(context.state, "Hello, Hello, 世界! 🌍!");
}
#[test]
fn test_very_long_argument() {
let handler = HelloCommand;
let mut context = TestContext::default();
let long_name = "x".repeat(10000);
let args = scalar_args([("name", long_name.as_str())]);
let result = handler.execute(&mut context, &args);
assert!(result.is_ok());
assert!(context.state.contains(&long_name));
}
#[test]
fn test_shared_mutable_context() {
let handler1 = StatefulCommand;
let handler2 = StatefulCommand;
let mut context = TestContext::default();
let args1 = scalar_args([("value", "A")]);
handler1.execute(&mut context, &args1).unwrap();
let args2 = scalar_args([("value", "B")]);
handler2.execute(&mut context, &args2).unwrap();
assert_eq!(context.state, "AB");
}
#[test]
fn test_object_safety_compile_time() {
fn _accepts_trait_object(_: &dyn CommandHandler) {}
let handler = HelloCommand;
_accepts_trait_object(&handler);
}
#[allow(dead_code)]
fn test_no_generic_methods_documentation() {}
struct AsyncHelloCommand;
#[async_trait]
impl AsyncCommandHandler for AsyncHelloCommand {
async fn execute(
&self,
context: &mut dyn ExecutionContext,
args: &ParsedArgs,
) -> Result<()> {
let ctx = crate::context::downcast_mut::<TestContext>(context).ok_or_else(|| {
ExecutionError::CommandFailed(anyhow::anyhow!("Wrong context type"))
})?;
let name = args.get_scalar("name").unwrap_or("World");
ctx.state = format!("Hello, {}!", name);
Ok(())
}
}
struct AsyncValidatedCommand;
#[async_trait]
impl AsyncCommandHandler for AsyncValidatedCommand {
async fn execute(
&self,
_context: &mut dyn ExecutionContext,
_args: &ParsedArgs,
) -> Result<()> {
Ok(())
}
async fn validate(&self, args: &ParsedArgs) -> Result<()> {
if args.get_scalar("count").is_none() {
return Err(
ExecutionError::CommandFailed(anyhow::anyhow!("count is required")).into(),
);
}
Ok(())
}
}
struct AsyncFailingCommand;
#[async_trait]
impl AsyncCommandHandler for AsyncFailingCommand {
async fn execute(
&self,
_context: &mut dyn ExecutionContext,
_args: &ParsedArgs,
) -> Result<()> {
Err(ExecutionError::CommandFailed(anyhow::anyhow!("Simulated async failure")).into())
}
}
#[test]
fn test_async_basic_execution() {
let handler = AsyncHelloCommand;
let mut context = TestContext::default();
let args = scalar_args([("name", "Rust")]);
let result = futures::executor::block_on(handler.execute(&mut context, &args));
assert!(result.is_ok());
assert_eq!(context.state, "Hello, Rust!");
}
#[test]
fn test_async_default_validation_accepts_all() {
let handler = AsyncHelloCommand;
let args = scalar_args([("random", "value")]);
let result = futures::executor::block_on(handler.validate(&args));
assert!(result.is_ok());
}
#[test]
fn test_async_custom_validation_missing_arg() {
let handler = AsyncValidatedCommand;
let args = ParsedArgs::from_scalars(HashMap::new());
let result = futures::executor::block_on(handler.validate(&args));
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("required"));
}
#[test]
fn test_async_custom_validation_success() {
let handler = AsyncValidatedCommand;
let args = scalar_args([("count", "5")]);
let result = futures::executor::block_on(handler.validate(&args));
assert!(result.is_ok());
}
#[test]
fn test_async_execution_failure() {
let handler = AsyncFailingCommand;
let mut context = TestContext::default();
let args = ParsedArgs::from_scalars(HashMap::new());
let result = futures::executor::block_on(handler.execute(&mut context, &args));
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(err_msg.contains("Simulated async failure"));
}
#[test]
fn test_async_trait_object_usage() {
let handler: Box<dyn AsyncCommandHandler> = Box::new(AsyncHelloCommand);
let mut context = TestContext::default();
let args = scalar_args([("name", "TraitObject")]);
let result = futures::executor::block_on(handler.execute(&mut context, &args));
assert!(result.is_ok());
assert_eq!(context.state, "Hello, TraitObject!");
}
#[test]
fn test_async_send_sync_requirement() {
let handler: Arc<dyn AsyncCommandHandler> = Arc::new(AsyncHelloCommand);
let handler_clone = handler.clone();
let _ = std::thread::spawn(move || {
let _h = handler_clone;
});
}
#[test]
fn test_async_object_safety_compile_time() {
fn _accepts_trait_object(_: &dyn AsyncCommandHandler) {}
_accepts_trait_object(&AsyncHelloCommand);
}
}