use async_trait::async_trait;
pub trait Command: Send + Sync {
type Result: Send + Sync;
}
#[async_trait]
pub trait CommandHandler<C: Command>: Send + Sync {
type Error: std::error::Error + Send + Sync;
async fn handle(&self, command: C) -> Result<C::Result, Self::Error>;
}
#[async_trait]
pub trait CommandDispatcher: Send + Sync {
async fn dispatch<C: Command>(
&self,
command: C,
) -> Result<C::Result, Box<dyn std::error::Error + Send + Sync>>;
}
pub trait ValidatableCommand: Command {
type ValidationError: std::error::Error + Send + Sync;
fn validate(&self) -> Result<(), Self::ValidationError>;
}
#[async_trait]
pub trait ValidatingCommandHandler<C: ValidatableCommand + 'static>: CommandHandler<C> {
async fn handle_validated(&self, command: C) -> Result<C::Result, Self::Error> {
self.handle(command).await
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestCommand {
value: i32,
}
impl Command for TestCommand {
type Result = i32;
}
struct TestHandler;
#[async_trait]
impl CommandHandler<TestCommand> for TestHandler {
type Error = std::io::Error;
async fn handle(&self, command: TestCommand) -> Result<i32, Self::Error> {
Ok(command.value * 2)
}
}
#[tokio::test]
async fn test_command_handler() {
let handler = TestHandler;
let command = TestCommand { value: 21 };
let result = handler.handle(command).await.unwrap();
assert_eq!(result, 42);
}
}