use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
Quit,
NextTab,
PrevTab,
JumpTab(usize),
Refresh,
ToggleHelp,
StartFilter,
ScrollUp,
ScrollDown,
ScrollTop,
ScrollBottom,
PageUp,
PageDown,
Select,
Back,
ToggleFollow,
Edit,
Toggle,
Delete,
Sort,
}
pub fn resolve(key: KeyEvent) -> Option<Action> {
if key.modifiers.contains(KeyModifiers::CONTROL) {
return match key.code {
KeyCode::Char('c' | 'q') => Some(Action::Quit),
_ => None,
};
}
match key.code {
KeyCode::Char('q') => Some(Action::Quit),
KeyCode::Tab => Some(Action::NextTab),
KeyCode::BackTab => Some(Action::PrevTab),
KeyCode::Char('1') => Some(Action::JumpTab(0)),
KeyCode::Char('2') => Some(Action::JumpTab(1)),
KeyCode::Char('3') => Some(Action::JumpTab(2)),
KeyCode::Char('4') => Some(Action::JumpTab(3)),
KeyCode::Char('5') => Some(Action::JumpTab(4)),
KeyCode::Char('6') => Some(Action::JumpTab(5)),
KeyCode::Char('7') => Some(Action::JumpTab(6)),
KeyCode::Char('8') => Some(Action::JumpTab(7)),
KeyCode::Char('9') => Some(Action::JumpTab(8)),
KeyCode::Char('0') => Some(Action::JumpTab(9)),
KeyCode::Char('r') => Some(Action::Refresh),
KeyCode::Char('?') => Some(Action::ToggleHelp),
KeyCode::Char('/') => Some(Action::StartFilter),
KeyCode::Char('j') | KeyCode::Down => Some(Action::ScrollDown),
KeyCode::Char('k') | KeyCode::Up => Some(Action::ScrollUp),
KeyCode::Char('g') => Some(Action::ScrollTop),
KeyCode::Char('G') => Some(Action::ScrollBottom),
KeyCode::PageUp => Some(Action::PageUp),
KeyCode::PageDown => Some(Action::PageDown),
KeyCode::Enter => Some(Action::Select),
KeyCode::Esc => Some(Action::Back),
KeyCode::Char('f') => Some(Action::ToggleFollow),
KeyCode::Char('e') => Some(Action::Edit),
KeyCode::Char('t') => Some(Action::Toggle),
KeyCode::Char('d') => Some(Action::Delete),
KeyCode::Char('s') => Some(Action::Sort),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
fn key(code: KeyCode) -> KeyEvent {
KeyEvent {
code,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}
}
fn ctrl(code: KeyCode) -> KeyEvent {
KeyEvent {
code,
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}
}
#[test]
fn q_maps_to_quit() {
assert_eq!(resolve(key(KeyCode::Char('q'))), Some(Action::Quit));
}
#[test]
fn ctrl_c_maps_to_quit() {
assert_eq!(resolve(ctrl(KeyCode::Char('c'))), Some(Action::Quit));
}
#[test]
fn ctrl_q_maps_to_quit() {
assert_eq!(resolve(ctrl(KeyCode::Char('q'))), Some(Action::Quit));
}
#[test]
fn tab_maps_to_next_tab() {
assert_eq!(resolve(key(KeyCode::Tab)), Some(Action::NextTab));
}
#[test]
fn backtab_maps_to_prev_tab() {
assert_eq!(resolve(key(KeyCode::BackTab)), Some(Action::PrevTab));
}
#[test]
fn digit_keys_map_to_jump_tab() {
assert_eq!(resolve(key(KeyCode::Char('1'))), Some(Action::JumpTab(0)));
assert_eq!(resolve(key(KeyCode::Char('2'))), Some(Action::JumpTab(1)));
assert_eq!(resolve(key(KeyCode::Char('3'))), Some(Action::JumpTab(2)));
assert_eq!(resolve(key(KeyCode::Char('4'))), Some(Action::JumpTab(3)));
assert_eq!(resolve(key(KeyCode::Char('5'))), Some(Action::JumpTab(4)));
assert_eq!(resolve(key(KeyCode::Char('6'))), Some(Action::JumpTab(5)));
assert_eq!(resolve(key(KeyCode::Char('7'))), Some(Action::JumpTab(6)));
assert_eq!(resolve(key(KeyCode::Char('8'))), Some(Action::JumpTab(7)));
assert_eq!(resolve(key(KeyCode::Char('9'))), Some(Action::JumpTab(8)));
assert_eq!(resolve(key(KeyCode::Char('0'))), Some(Action::JumpTab(9)));
}
#[test]
fn r_maps_to_refresh() {
assert_eq!(resolve(key(KeyCode::Char('r'))), Some(Action::Refresh));
}
#[test]
fn question_mark_maps_to_toggle_help() {
assert_eq!(resolve(key(KeyCode::Char('?'))), Some(Action::ToggleHelp));
}
#[test]
fn slash_maps_to_start_filter() {
assert_eq!(resolve(key(KeyCode::Char('/'))), Some(Action::StartFilter));
}
#[test]
fn navigation_keys() {
assert_eq!(resolve(key(KeyCode::Char('j'))), Some(Action::ScrollDown));
assert_eq!(resolve(key(KeyCode::Down)), Some(Action::ScrollDown));
assert_eq!(resolve(key(KeyCode::Char('k'))), Some(Action::ScrollUp));
assert_eq!(resolve(key(KeyCode::Up)), Some(Action::ScrollUp));
assert_eq!(resolve(key(KeyCode::Char('g'))), Some(Action::ScrollTop));
assert_eq!(resolve(key(KeyCode::Char('G'))), Some(Action::ScrollBottom));
assert_eq!(resolve(key(KeyCode::PageUp)), Some(Action::PageUp));
assert_eq!(resolve(key(KeyCode::PageDown)), Some(Action::PageDown));
}
#[test]
fn action_keys() {
assert_eq!(resolve(key(KeyCode::Enter)), Some(Action::Select));
assert_eq!(resolve(key(KeyCode::Esc)), Some(Action::Back));
assert_eq!(resolve(key(KeyCode::Char('f'))), Some(Action::ToggleFollow));
assert_eq!(resolve(key(KeyCode::Char('e'))), Some(Action::Edit));
assert_eq!(resolve(key(KeyCode::Char('t'))), Some(Action::Toggle));
assert_eq!(resolve(key(KeyCode::Char('d'))), Some(Action::Delete));
assert_eq!(resolve(key(KeyCode::Char('s'))), Some(Action::Sort));
}
#[test]
fn unrecognized_key_returns_none() {
assert_eq!(resolve(key(KeyCode::Char('z'))), None);
assert_eq!(resolve(key(KeyCode::F(1))), None);
assert_eq!(resolve(key(KeyCode::Insert)), None);
}
#[test]
fn ctrl_with_unrecognized_char_returns_none() {
assert_eq!(resolve(ctrl(KeyCode::Char('x'))), None);
assert_eq!(resolve(ctrl(KeyCode::Char('a'))), None);
}
}