//! Slash-command parser that maps TUI `/foo` input lines into `Command` //! variants for the action dispatch system. /// A parsed slash command from the TUI input buffer. #[derive(Debug, Clone, PartialEq)] pub enum Command { Help, Quit, McpOpen, Clear, ClearConfirm, Login { provider: String }, Edit(String), McpAdd { name: String, command: String }, ModelList, Compact, TodoOpen, UsageOpen, Unknown(String), } /// Parse a slash-prefixed input line into a `Command` value. /// /// Flow: trim -> check for leading `/` -> split on space (max 3 parts) -> /// match the first token against known commands -> extract arguments from /// the remaining parts. /// /// Why: early return `Unknown` for non-slash lines so the caller can treat /// them as regular chat input. pub fn parse_command(text: &str) -> Command { let text = text.trim(); if !text.starts_with('/') { return Command::Unknown(text.to_string()); } let parts: Vec<&str> = text.splitn(3, ' ').collect(); let cmd = parts[0]; let arg1 = parts.get(1).copied().unwrap_or(""); let arg2 = parts.get(2).copied().unwrap_or(""); match cmd { "/help" => Command::Help, "/quit" => Command::Quit, "/clear" if arg1.is_empty() => Command::ClearConfirm, "/clear" => Command::Clear, "/login" if arg1.is_empty() => Command::Login { provider: String::new(), }, "/login" if !arg1.is_empty() => Command::Login { provider: arg1.to_string(), }, "/edit" if !arg1.is_empty() => Command::Edit(arg1.to_string()), "/edit" => Command::Edit(".".to_string()), "/mcp" if arg1.is_empty() => Command::McpOpen, "/mcp" if arg1 == "add" && !arg2.is_empty() => { let rest = arg2.trim(); if let Some(space) = rest.find(' ') { let name = rest[..space].to_string(); let command = rest[space + 1..].trim().to_string(); Command::McpAdd { name, command } } else { Command::McpAdd { name: rest.to_string(), command: String::new(), } } } "/model" => Command::ModelList, "/compact" => Command::Compact, "/todo" => Command::TodoOpen, "/usage" => Command::UsageOpen, _ => Command::Unknown(cmd.to_string()), } } #[cfg(test)] mod tests { use super::*; #[test] fn parses_todo_open() { assert_eq!(parse_command("/todo"), Command::TodoOpen); } #[test] fn parses_usage_open() { assert_eq!(parse_command("/usage"), Command::UsageOpen); } }