Skip to main content

studiole_command/macros/
server_macro.rs

1use crate::prelude::*;
2
3#[macro_export]
4macro_rules! define_commands_server {
5    ($($kind:ident($req:ty, $handler:ty)),* $(,)?) => {
6        #[derive(Clone)]
7        pub enum CommandHandler {
8            $(
9                $kind(Arc<$handler>),
10            )*
11        }
12
13        impl IHandler for CommandHandler {}
14
15        pub enum Command {
16            $(
17                $kind($req, Arc<$handler>),
18            )*
19        }
20
21        #[async_trait]
22        impl ICommand<CommandHandler, CommandSuccess, CommandFailure> for Command {
23            fn new<T: Executable + Send + Sync + 'static>(
24                request: T,
25                handler: CommandHandler,
26            ) -> Self {
27                let request_any: Box<dyn Any> = Box::new(request);
28                match handler {
29                    $(
30                    CommandHandler::$kind(handler) => {
31                        let request = request_any
32                            .downcast::<$req>()
33                            .expect("Request type should match handler");
34                        Self::$kind(*request, handler)
35                    },
36                    )*
37                }
38            }
39
40            async fn execute(self) -> Result<CommandSuccess, CommandFailure> {
41                match self {
42                    $(
43                        Self::$kind(request, handler) => {
44                            match handler.execute(&request).await {
45                                Ok(result) => Ok(CommandSuccess::$kind(result)),
46                                Err(e) => Err(CommandFailure::$kind(e)),
47                            }
48                        },
49                    )*
50                }
51            }
52        }
53
54        impl Display for Command {
55            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
56                let name = match &self {
57                    $(
58                        Self::$kind(request, _) => request.to_string(),
59                    )*
60                };
61                f.write_str(&name)
62            }
63        }
64
65        $(
66            impl From<Arc<$handler>> for CommandHandler {
67                fn from(handler: Arc<$handler>) -> Self {
68                    Self::$kind(handler)
69                }
70            }
71        )*
72
73        pub trait WithCommands: Sized {
74            fn with_commands(self) -> impl Future<Output = Result<Self, Report<ServiceError>>> + Send;
75        }
76
77        impl WithCommands for ServiceProvider {
78            async fn with_commands(self) -> Result<Self, Report<ServiceError>> {
79                let mut registry: CommandRegistry<CommandInfo> = CommandRegistry::new();
80                $(
81                    let handler = self.get_service::<$handler>().await?;
82                    registry.register::<$req, $handler>(handler);
83                )*
84                Ok(self.with_instance(registry))
85            }
86        }
87    };
88}
89
90/// Marker trait for command handler enums generated by [`define_commands_server`].
91pub trait IHandler: Clone + Send + Sync {}
92
93/// A command that pairs a request with its handler for execution.
94#[async_trait]
95pub trait ICommand<H: IHandler, S: ISuccess, F: IFailure>: Display + Send + Sync {
96    /// Create a command from a request and its matched handler.
97    fn new<T: Executable + Send + Sync + 'static>(request: T, handler: H) -> Self;
98    /// Execute the command and return the result.
99    async fn execute(self) -> Result<S, F>;
100}