Skip to main content

studiole_command/services/
command_registry.rs

1use crate::prelude::*;
2
3/// Map request types to their handlers.
4pub struct CommandRegistry<T: ICommandInfo> {
5    handlers: HashMap<TypeId, T::Handler>,
6}
7
8impl<T: ICommandInfo> CommandRegistry<T> {
9    /// Create an empty [`CommandRegistry`].
10    #[must_use]
11    pub fn new() -> Self {
12        Self {
13            handlers: HashMap::default(),
14        }
15    }
16
17    /// Register a handler for a request type.
18    #[allow(clippy::as_conversions)]
19    pub fn register<
20        R: Executable + Send + Sync + 'static,
21        H: Execute<R, R::Response, R::ExecutionError>,
22    >(
23        &mut self,
24        handler: Arc<H>,
25    ) where
26        Arc<H>: Into<T::Handler>,
27    {
28        let request_type = TypeId::of::<R>();
29        self.handlers.insert(request_type, handler.into());
30    }
31
32    /// Resolve a request into a command by matching it to a registered handler.
33    #[allow(clippy::as_conversions)]
34    pub fn resolve<R: Executable + Send + Sync + 'static>(
35        &self,
36        request: R,
37    ) -> Result<T::Command, Report<QueueError>> {
38        let request_type = TypeId::of::<R>();
39        let handler = self
40            .handlers
41            .get(&request_type)
42            .ok_or_else(|| {
43                Report::new(QueueError::NoMatch)
44                    .attach_with("request_type", || String::from(type_name::<R>()))
45                    .attach("request", request.to_string())
46            })?
47            .clone();
48        let command = T::Command::new(request, handler);
49        Ok(command)
50    }
51}
52
53impl<T: ICommandInfo + 'static> Service for CommandRegistry<T> {
54    type Error = ServiceError;
55
56    async fn from_services(_services: &ServiceProvider) -> Result<Self, Report<Self::Error>> {
57        let registry = CommandRegistry::new();
58        Ok(registry)
59    }
60}
61
62/// Errors returned by [`CommandRegistry::resolve`].
63#[derive(Debug, Error)]
64pub enum QueueError {
65    #[error("Unable to match request to command")]
66    NoMatch,
67    #[error("Unable to match request to command")]
68    IncorrectCommandType,
69}