busstop/
command.rs

1mod command_handler;
2mod dispatched_command;
3
4use std::sync::Arc;
5
6pub use command_handler::CommandHandler;
7pub use dispatched_command::DispatchedCommand;
8use futures::future::BoxFuture;
9use simple_middleware::{Manager as MiddlewareManager, Next};
10
11use crate::Busstop;
12
13/// Next middleware to call. Send argument pass to all commands' middlewares
14pub type NextCommandMiddleware = Next<DispatchedCommand, DispatchedCommand>;
15
16/// Command middleware type
17pub type CommandMiddleware = Box<
18    dyn FnMut(DispatchedCommand, NextCommandMiddleware) -> BoxFuture<'static, DispatchedCommand>
19        + Send
20        + Sync,
21>;
22
23/// A type that can be used as a command can implement this trait.
24/// Implementing this trait makes it easy to register an handler
25/// and to dispatch the command.
26#[async_trait::async_trait]
27pub trait DispatchableCommand: Send + Sync {
28    /// Dispatch the command
29    async fn dispatch_command(self) -> bool
30    where
31        Self: Sized + 'static,
32    {
33        Busstop::instance().dispatch_command(self).await
34    }
35
36    /// Register this handler for this command
37    async fn command_handler<H: CommandHandler + Default + 'static>()
38    where
39        Self: Sized,
40    {
41        Busstop::instance()
42            .register_command::<Self>(H::default())
43            .await;
44    }
45
46    /// Register a middleware on this dispatchable command
47    async fn command_middleware<M: 'static>(middleware: M)
48    where
49        Self: Sized,
50        M: FnMut(
51                DispatchedCommand,
52                Next<DispatchedCommand, DispatchedCommand>,
53            ) -> BoxFuture<'static, DispatchedCommand>
54            + Send
55            + Sync,
56    {
57        Busstop::instance()
58            .register_command_middleware::<Self, M>(middleware)
59            .await;
60    }
61
62    /// Register this handler if the command does not have an existing handler
63    async fn soft_command_handler<H: CommandHandler + Default + 'static>()
64    where
65        Self: Sized,
66    {
67        let bus = Busstop::instance();
68        if !bus.command_has_handler::<Self>().await {
69            bus.register_command::<Self>(H::default()).await;
70        }
71    }
72
73    /// Register the instance as the handler for this command
74    async fn register_command_handler<H: CommandHandler + 'static>(handler: H)
75    where
76        Self: Sized,
77    {
78        Busstop::instance().register_command::<Self>(handler).await;
79    }
80
81    /// Register the instance as the soft handler for this command
82    async fn register_soft_command_handler<H: CommandHandler + 'static>(handler: H)
83    where
84        Self: Sized,
85    {
86        let bus = Busstop::instance();
87        if !bus.command_has_handler::<Self>().await {
88            bus.register_command::<Self>(handler).await;
89        }
90    }
91}
92
93/// Manages the middlewares for the current command handler
94pub struct CommandHandlerManager {
95    name: String,
96    middleware: MiddlewareManager<DispatchedCommand, DispatchedCommand>,
97}
98
99impl CommandHandlerManager {
100    /// Create a new instance
101    pub async fn new(handler: impl CommandHandler + 'static) -> Self {
102        let handler = Arc::new(Box::new(handler));
103        Self {
104            name: handler.command_handler_name().to_string(),
105            middleware: MiddlewareManager::last(move |dispatched, _| {
106                let instance = handler.clone();
107                Box::pin(async move { instance.clone().handle_command(dispatched).await })
108            })
109            .await,
110        }
111    }
112
113    /// The name of the command
114    pub fn name(&self) -> &String {
115        &self.name
116    }
117
118    pub async fn next<M>(&self, middleware: M) -> &Self
119    where
120        M: FnMut(DispatchedCommand, NextCommandMiddleware) -> BoxFuture<'static, DispatchedCommand>
121            + Send
122            + 'static,
123    {
124        self.middleware.next(middleware).await;
125        self
126    }
127
128    pub async fn handle(&self, dispatched: DispatchedCommand) -> DispatchedCommand {
129        let mut result = self.middleware.send(dispatched).await;
130        result.handled = true;
131
132        result
133    }
134
135    pub async fn handle_command<C: Send + Sync + 'static>(&self, command: C) -> DispatchedCommand {
136        self.handle(DispatchedCommand::new(
137            Box::new(command),
138            std::any::type_name::<C>(),
139        ))
140        .await
141    }
142}
143
144#[cfg(test)]
145mod test {
146    use super::*;
147
148    struct Cmd;
149    impl DispatchableCommand for Cmd {}
150
151    #[derive(Default)]
152    struct CmdHandler;
153
154    #[async_trait::async_trait]
155    impl CommandHandler for CmdHandler {
156        async fn handle_command(&self, c: DispatchedCommand) -> DispatchedCommand {
157            c
158        }
159    }
160
161    #[tokio::test]
162    async fn test_command_handler_manager() {
163        let manager = CommandHandlerManager::new(CmdHandler).await;
164
165        manager
166            .next(|c, n| Box::pin(async move { n.call(c).await }))
167            .await;
168        manager
169            .next(|c, n| Box::pin(async move { n.call(c).await }))
170            .await;
171
172        assert_eq!(manager.handle_command(Cmd).await.handled(), true)
173    }
174}