Skip to main content

nut_shell/shell/
handler.rs

1//! Command handler trait for executing commands.
2//!
3//! Maps command IDs to execution functions, implementing the execution side
4//! of the metadata/execution separation pattern.
5
6use crate::config::ShellConfig;
7use crate::error::CliError;
8use crate::response::Response;
9
10/// Command execution handler trait.
11/// Maps command IDs to execution functions (dispatches on unique ID, not display name).
12pub trait CommandHandler<C: ShellConfig> {
13    /// Execute synchronous command by unique ID.
14    fn execute_sync(&self, id: &str, args: &[&str]) -> Result<Response<C>, CliError>;
15
16    /// Execute asynchronous command by unique ID (requires `async` feature).
17    /// Uses `async fn` without Send bounds for both single and multi-threaded executors.
18    #[cfg(feature = "async")]
19    #[allow(async_fn_in_trait)]
20    async fn execute_async(&self, id: &str, args: &[&str]) -> Result<Response<C>, CliError>;
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26    use crate::config::DefaultConfig;
27
28    // Mock handler for testing
29    struct TestHandler;
30
31    impl CommandHandler<DefaultConfig> for TestHandler {
32        fn execute_sync(
33            &self,
34            id: &str,
35            _args: &[&str],
36        ) -> Result<Response<DefaultConfig>, CliError> {
37            match id {
38                "test" => Ok(Response::success("OK")),
39                _ => Err(CliError::CommandNotFound),
40            }
41        }
42
43        #[cfg(feature = "async")]
44        async fn execute_async(
45            &self,
46            id: &str,
47            _args: &[&str],
48        ) -> Result<Response<DefaultConfig>, CliError> {
49            match id {
50                "async-test" => Ok(Response::success("Async OK")),
51                _ => Err(CliError::CommandNotFound),
52            }
53        }
54    }
55
56    #[test]
57    fn test_sync_handler() {
58        let handler = TestHandler;
59        let result = handler.execute_sync("test", &[]);
60        assert!(result.is_ok());
61        assert_eq!(result.unwrap().message.as_str(), "OK");
62
63        let result = handler.execute_sync("unknown", &[]);
64        assert_eq!(result, Err(CliError::CommandNotFound));
65    }
66
67    #[cfg(feature = "async")]
68    #[tokio::test]
69    async fn test_async_handler() {
70        let handler = TestHandler;
71        let result = handler.execute_async("async-test", &[]).await;
72        assert!(result.is_ok());
73        assert_eq!(result.unwrap().message.as_str(), "Async OK");
74
75        let result = handler.execute_async("unknown", &[]).await;
76        assert_eq!(result, Err(CliError::CommandNotFound));
77    }
78}