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        impl RegisterHandlers for CommandInfo {
74            async fn register_handlers(
75                services: &ServiceProvider,
76            ) -> Result<CommandRegistry<Self>, Report<ResolveError>> {
77                let mut registry = CommandRegistry::new();
78                $(
79                    let handler = services.get_async::<$handler>().await?;
80                    registry.register::<$req, $handler>(handler);
81                )*
82                Ok(registry)
83            }
84        }
85
86        pub trait WithCommands: Sized {
87            fn with_commands(self) -> Self;
88        }
89
90        impl WithCommands for ServiceBuilder {
91            fn with_commands(self) -> Self {
92                self
93                    $(
94                        .with_type_async::<$handler>()
95                    )*
96                    .with_type_async::<CommandRegistry<CommandInfo>>()
97                    .with_type_async::<CommandRunner<CommandInfo>>()
98                    .with_type::<CommandMediator<CommandInfo>>()
99                    .with_type::<WorkerPool<CommandInfo>>()
100                    .with_type::<CommandEvents<CommandInfo>>()
101                    .with_type::<CliProgress<CommandInfo>>()
102            }
103        }
104    };
105}
106
107/// Marker trait for command handler enums generated by [`define_commands_server`].
108pub trait IHandler: Clone + Send + Sync {}
109
110/// A command that pairs a request with its handler for execution.
111#[async_trait]
112pub trait ICommand<H: IHandler, S: ISuccess, F: IFailure>: Display + Send + Sync {
113    /// Create a command from a request and its matched handler.
114    fn new<T: Executable + Send + Sync + 'static>(request: T, handler: H) -> Self;
115    /// Execute the command and return the result.
116    async fn execute(self) -> Result<S, F>;
117}