agent_air_tui/commands/custom.rs
1//! Custom command helper for closure-based commands.
2
3use super::context::CommandContext;
4use super::result::CommandResult;
5use super::traits::SlashCommand;
6
7/// Closure type for custom command handlers.
8pub type CommandHandlerFn = Box<dyn Fn(&str, &mut CommandContext) -> CommandResult + Send + Sync>;
9
10/// A simple custom command using a closure.
11///
12/// Use this for commands that don't need internal state.
13/// For stateful commands, implement [`SlashCommand`] directly.
14///
15/// # Example
16///
17/// ```ignore
18/// use agent_air::tui::commands::{CustomCommand, CommandResult};
19///
20/// let cmd = CustomCommand::new(
21/// "greet",
22/// "Say hello to someone",
23/// |args, ctx| {
24/// let name = if args.is_empty() { "World" } else { args };
25/// CommandResult::Message(format!("Hello, {}!", name))
26/// },
27/// );
28/// ```
29pub struct CustomCommand {
30 name: String,
31 description: String,
32 handler: CommandHandlerFn,
33}
34
35impl CustomCommand {
36 /// Create a new custom command.
37 ///
38 /// # Arguments
39 /// * `name` - Command name without the leading slash
40 /// * `description` - Short description shown in the popup
41 /// * `handler` - Closure that executes the command
42 pub fn new<F>(name: impl Into<String>, description: impl Into<String>, handler: F) -> Self
43 where
44 F: Fn(&str, &mut CommandContext) -> CommandResult + Send + Sync + 'static,
45 {
46 Self {
47 name: name.into(),
48 description: description.into(),
49 handler: Box::new(handler),
50 }
51 }
52}
53
54impl SlashCommand for CustomCommand {
55 fn name(&self) -> &str {
56 &self.name
57 }
58
59 fn description(&self) -> &str {
60 &self.description
61 }
62
63 fn execute(&self, args: &str, ctx: &mut CommandContext) -> CommandResult {
64 (self.handler)(args, ctx)
65 }
66}