use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use super::state::View;
#[derive(Debug, Clone, Default)]
pub struct CommandInput {
buffer: String,
cursor: usize,
}
impl CommandInput {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.buffer.clear();
self.cursor = 0;
}
pub fn buffer(&self) -> &str {
&self.buffer
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn handle_key(&mut self, key: KeyEvent) -> CommandKeyResult {
match (key.code, key.modifiers) {
(KeyCode::Enter, _) => {
let cmd = self.buffer.clone();
self.clear();
CommandKeyResult::Execute(cmd)
}
(KeyCode::Esc, _) => {
self.clear();
CommandKeyResult::Cancel
}
(KeyCode::Backspace, _) => {
if self.cursor > 0 {
self.cursor -= 1;
self.buffer.remove(self.cursor);
CommandKeyResult::Continue
} else if self.buffer.is_empty() {
CommandKeyResult::Cancel
} else {
CommandKeyResult::Continue
}
}
(KeyCode::Delete, _) => {
if self.cursor < self.buffer.len() {
self.buffer.remove(self.cursor);
}
CommandKeyResult::Continue
}
(KeyCode::Left, _) => {
self.cursor = self.cursor.saturating_sub(1);
CommandKeyResult::Continue
}
(KeyCode::Right, _) => {
self.cursor = (self.cursor + 1).min(self.buffer.len());
CommandKeyResult::Continue
}
(KeyCode::Home, _) | (KeyCode::Char('a'), KeyModifiers::CONTROL) => {
self.cursor = 0;
CommandKeyResult::Continue
}
(KeyCode::End, _) | (KeyCode::Char('e'), KeyModifiers::CONTROL) => {
self.cursor = self.buffer.len();
CommandKeyResult::Continue
}
(KeyCode::Char('u'), KeyModifiers::CONTROL) => {
self.clear();
CommandKeyResult::Continue
}
(KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => {
self.buffer.insert(self.cursor, c);
self.cursor += 1;
CommandKeyResult::Continue
}
_ => CommandKeyResult::Continue,
}
}
}
#[derive(Debug, Clone)]
pub enum CommandKeyResult {
Continue,
Cancel,
Execute(String),
}
#[derive(Debug, Clone)]
pub enum CommandAction {
None,
Quit,
Refresh,
NavigateTo(String),
GoToRoot,
NavigateBack,
ShowHelp,
SwitchView(View),
ClearMarks,
ToggleDetails,
SetTheme(ThemeCommand),
}
#[derive(Debug, Clone, Copy)]
pub enum ThemeCommand {
Dark,
Light,
Toggle,
}
pub fn parse_command(cmd: &str) -> CommandAction {
let parts: Vec<&str> = cmd.trim().split_whitespace().collect();
if parts.is_empty() {
return CommandAction::None;
}
match parts[0] {
"q" | "quit" | "exit" => CommandAction::Quit,
"r" | "refresh" | "rescan" => CommandAction::Refresh,
"cd" => {
if parts.len() > 1 {
CommandAction::NavigateTo(parts[1..].join(" "))
} else {
CommandAction::GoToRoot
}
}
"root" | "top" => CommandAction::GoToRoot,
"back" | "up" | ".." => CommandAction::NavigateBack,
"help" | "?" => CommandAction::ShowHelp,
"explorer" | "e" | "tree" => CommandAction::SwitchView(View::Explorer),
"duplicates" | "dups" | "d" => CommandAction::SwitchView(View::Duplicates),
"age" | "a" => CommandAction::SwitchView(View::Age),
"errors" | "err" => CommandAction::SwitchView(View::Errors),
"clear" | "unmark" => CommandAction::ClearMarks,
"details" | "info" | "i" => CommandAction::ToggleDetails,
"theme" | "t" => {
if parts.len() > 1 {
match parts[1] {
"dark" => CommandAction::SetTheme(ThemeCommand::Dark),
"light" => CommandAction::SetTheme(ThemeCommand::Light),
"toggle" => CommandAction::SetTheme(ThemeCommand::Toggle),
_ => CommandAction::None,
}
} else {
CommandAction::SetTheme(ThemeCommand::Toggle)
}
}
"dark" => CommandAction::SetTheme(ThemeCommand::Dark),
"light" => CommandAction::SetTheme(ThemeCommand::Light),
_ => CommandAction::None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_quit() {
assert!(matches!(parse_command("q"), CommandAction::Quit));
assert!(matches!(parse_command("quit"), CommandAction::Quit));
assert!(matches!(parse_command("exit"), CommandAction::Quit));
}
#[test]
fn test_parse_cd() {
assert!(matches!(parse_command("cd"), CommandAction::GoToRoot));
match parse_command("cd /some/path") {
CommandAction::NavigateTo(path) => assert_eq!(path, "/some/path"),
_ => panic!("Expected NavigateTo"),
}
}
#[test]
fn test_parse_view() {
assert!(matches!(
parse_command("explorer"),
CommandAction::SwitchView(View::Explorer)
));
assert!(matches!(
parse_command("d"),
CommandAction::SwitchView(View::Duplicates)
));
}
}