use crate::prelude::*;
pub struct CommandRegistry<T: ICommandInfo> {
handlers: HashMap<TypeId, T::Handler>,
}
impl<T: ICommandInfo> CommandRegistry<T> {
#[must_use]
pub fn new() -> Self {
Self {
handlers: HashMap::default(),
}
}
#[allow(clippy::as_conversions)]
pub fn register<
R: Executable + Send + Sync + 'static,
H: Execute<R, R::Response, R::ExecutionError>,
>(
&mut self,
handler: Arc<H>,
) where
Arc<H>: Into<T::Handler>,
{
let request_type = TypeId::of::<R>();
self.handlers.insert(request_type, handler.into());
}
#[allow(clippy::as_conversions)]
pub fn resolve<R: Executable + Send + Sync + 'static>(
&self,
request: R,
) -> Result<T::Command, Report<QueueError>> {
let request_type = TypeId::of::<R>();
let handler = self
.handlers
.get(&request_type)
.ok_or_else(|| {
Report::new(QueueError::NoMatch)
.attach_with("request_type", || String::from(type_name::<R>()))
.attach("request", request.to_string())
})?
.clone();
let command = T::Command::new(request, handler);
Ok(command)
}
}
pub trait RegisterHandlers: ICommandInfo {
fn register_handlers(
services: &ServiceProvider,
) -> impl Future<Output = Result<CommandRegistry<Self>, Report<ResolveError>>> + Send
where
Self: Sized;
}
impl<T: RegisterHandlers + 'static> FromServicesAsync for CommandRegistry<T> {
type Error = ResolveError;
async fn from_services_async(services: &ServiceProvider) -> Result<Self, Report<Self::Error>> {
T::register_handlers(services).await
}
}
#[derive(Debug, Error)]
pub enum QueueError {
#[error("Unable to match request to command")]
NoMatch,
#[error("Unable to match request to command")]
IncorrectCommandType,
}