agent_core/tui/commands/
helpers.rs

1//! Helper functions for slash command parsing and filtering.
2
3use super::traits::SlashCommand;
4
5/// Check if input starts with a slash command.
6///
7/// # Example
8///
9/// ```ignore
10/// assert!(is_slash_command("/help"));
11/// assert!(!is_slash_command("hello"));
12/// ```
13pub fn is_slash_command(input: &str) -> bool {
14    input.starts_with('/')
15}
16
17/// Parse slash command from input, returning (command_name, args).
18///
19/// # Example
20///
21/// ```ignore
22/// assert_eq!(parse_command("/help"), Some(("help", "")));
23/// assert_eq!(parse_command("/echo hello"), Some(("echo", "hello")));
24/// assert_eq!(parse_command("not a command"), None);
25/// ```
26pub fn parse_command(input: &str) -> Option<(&str, &str)> {
27    if !is_slash_command(input) {
28        return None;
29    }
30
31    let trimmed = input.trim_start_matches('/');
32    let mut parts = trimmed.splitn(2, ' ');
33    let name = parts.next()?;
34    let args = parts.next().unwrap_or("").trim();
35
36    Some((name, args))
37}
38
39/// Filter commands that match the given input prefix.
40///
41/// Input can include or omit the leading slash.
42///
43/// # Example
44///
45/// ```ignore
46/// let matches = filter_commands(&commands, "/he");
47/// // Returns commands starting with "he" (e.g., "help")
48/// ```
49pub fn filter_commands<'a>(
50    commands: &'a [Box<dyn SlashCommand>],
51    input: &str,
52) -> Vec<&'a dyn SlashCommand> {
53    let search_term = input.trim_start_matches('/').to_lowercase();
54
55    commands
56        .iter()
57        .filter(|cmd| cmd.name().to_lowercase().starts_with(&search_term))
58        .map(|c| c.as_ref())
59        .collect()
60}
61
62/// Get a command by its exact name.
63///
64/// Name can include or omit the leading slash.
65pub fn get_command_by_name<'a>(
66    commands: &'a [Box<dyn SlashCommand>],
67    name: &str,
68) -> Option<&'a dyn SlashCommand> {
69    let name = name.trim_start_matches('/');
70    commands.iter().find(|cmd| cmd.name() == name).map(|c| c.as_ref())
71}
72
73/// Generate help message listing all available commands.
74pub fn generate_help_message(commands: &[Box<dyn SlashCommand>]) -> String {
75    let mut help = String::from("Available commands:\n\n");
76
77    for cmd in commands {
78        help.push_str(&format!("  /{} - {}\n", cmd.name(), cmd.description()));
79    }
80
81    help
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_is_slash_command() {
90        assert!(is_slash_command("/help"));
91        assert!(is_slash_command("/"));
92        assert!(!is_slash_command("help"));
93        assert!(!is_slash_command(""));
94    }
95
96    #[test]
97    fn test_parse_command() {
98        assert_eq!(parse_command("/help"), Some(("help", "")));
99        assert_eq!(parse_command("/echo hello world"), Some(("echo", "hello world")));
100        assert_eq!(parse_command("/cmd  spaced  args"), Some(("cmd", "spaced  args")));
101        assert_eq!(parse_command("not a command"), None);
102        assert_eq!(parse_command(""), None);
103    }
104}