Files
zesdex/src/controller/command.rs
T
asepharyanaandClaude Sonnet 5 aa2b6acb95 feat(tui): tambah command /todo dan /usage untuk buka overlay
Overlay Todo dan Usage sebelumnya tidak punya trigger sama sekali di
jalur interaksi normal (cuma bisa lewat restore snapshot sesi) --
sekarang mengikuti pola /workflow yang sudah ada.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 23:41:53 +07:00

100 lines
3.1 KiB
Rust

//! 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,
LessonInteractive,
McpOpen,
Clear,
ClearConfirm,
Login { provider: String },
Edit(String),
McpAdd {
name: String,
command: String,
},
ModelList,
Compact,
WorkflowOpen,
WorkflowRun {
script: String,
},
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,
"/lesson" => Command::LessonInteractive,
"/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,
"/workflow" if arg1.is_empty() => Command::WorkflowOpen,
"/workflow" if arg1 == "run" && !arg2.is_empty() => Command::WorkflowRun {
script: arg2.to_string(),
},
"/workflow" if arg1 == "run" => Command::WorkflowOpen,
"/workflow" => Command::WorkflowRun {
script: arg1.to_string(),
},
"/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);
}
}