use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyAction {
InsertChar(char),
Backspace,
Delete,
CursorLeft,
CursorRight,
CursorHome,
CursorEnd,
Evaluate,
Clear,
ClearAll,
RecallLast,
Quit,
None,
}
#[derive(Debug, Default)]
pub struct InputHandler;
impl InputHandler {
#[must_use]
pub fn new() -> Self {
Self
}
#[must_use]
pub fn handle_key(&self, event: KeyEvent) -> KeyAction {
let KeyEvent {
code, modifiers, ..
} = event;
if modifiers.contains(KeyModifiers::CONTROL) {
return match code {
KeyCode::Char('c' | 'q') => KeyAction::Quit,
KeyCode::Char('l') => KeyAction::ClearAll,
KeyCode::Char('a') => KeyAction::CursorHome,
KeyCode::Char('e') => KeyAction::CursorEnd,
KeyCode::Char('u') => KeyAction::Clear,
_ => KeyAction::None,
};
}
match code {
KeyCode::Char(c) => KeyAction::InsertChar(c),
KeyCode::Backspace => KeyAction::Backspace,
KeyCode::Delete => KeyAction::Delete,
KeyCode::Left => KeyAction::CursorLeft,
KeyCode::Right => KeyAction::CursorRight,
KeyCode::Home => KeyAction::CursorHome,
KeyCode::End => KeyAction::CursorEnd,
KeyCode::Enter => KeyAction::Evaluate,
KeyCode::Esc => KeyAction::Clear,
KeyCode::Up => KeyAction::RecallLast,
_ => KeyAction::None,
}
}
#[must_use]
pub fn is_valid_char(c: char) -> bool {
c.is_ascii_digit()
|| c == '.'
|| c == '+'
|| c == '-'
|| c == '*'
|| c == '/'
|| c == '%'
|| c == '^'
|| c == '('
|| c == ')'
|| c == ' '
}
}
#[cfg(test)]
mod tests {
use super::*;
fn key_event(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::NONE)
}
fn key_event_ctrl(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::CONTROL)
}
#[test]
fn test_input_handler_new() {
let handler = InputHandler::new();
let _ = format!("{:?}", handler);
}
#[test]
fn test_input_handler_default() {
let handler = InputHandler;
let _ = format!("{:?}", handler);
}
#[test]
fn test_handle_digit_keys() {
let handler = InputHandler::new();
for c in '0'..='9' {
let event = key_event(KeyCode::Char(c));
assert_eq!(handler.handle_key(event), KeyAction::InsertChar(c));
}
}
#[test]
fn test_handle_operator_keys() {
let handler = InputHandler::new();
let operators = ['+', '-', '*', '/', '%', '^'];
for c in operators {
let event = key_event(KeyCode::Char(c));
assert_eq!(handler.handle_key(event), KeyAction::InsertChar(c));
}
}
#[test]
fn test_handle_parentheses() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Char('('))),
KeyAction::InsertChar('(')
);
assert_eq!(
handler.handle_key(key_event(KeyCode::Char(')'))),
KeyAction::InsertChar(')')
);
}
#[test]
fn test_handle_decimal_point() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Char('.'))),
KeyAction::InsertChar('.')
);
}
#[test]
fn test_handle_space() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Char(' '))),
KeyAction::InsertChar(' ')
);
}
#[test]
fn test_handle_backspace() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Backspace)),
KeyAction::Backspace
);
}
#[test]
fn test_handle_delete() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Delete)),
KeyAction::Delete
);
}
#[test]
fn test_handle_left() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Left)),
KeyAction::CursorLeft
);
}
#[test]
fn test_handle_right() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Right)),
KeyAction::CursorRight
);
}
#[test]
fn test_handle_home() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Home)),
KeyAction::CursorHome
);
}
#[test]
fn test_handle_end() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::End)),
KeyAction::CursorEnd
);
}
#[test]
fn test_handle_enter() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Enter)),
KeyAction::Evaluate
);
}
#[test]
fn test_handle_escape() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Esc)),
KeyAction::Clear
);
}
#[test]
fn test_handle_up() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::Up)),
KeyAction::RecallLast
);
}
#[test]
fn test_handle_ctrl_c() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('c'))),
KeyAction::Quit
);
}
#[test]
fn test_handle_ctrl_q() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('q'))),
KeyAction::Quit
);
}
#[test]
fn test_handle_ctrl_l() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('l'))),
KeyAction::ClearAll
);
}
#[test]
fn test_handle_ctrl_a() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('a'))),
KeyAction::CursorHome
);
}
#[test]
fn test_handle_ctrl_e() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('e'))),
KeyAction::CursorEnd
);
}
#[test]
fn test_handle_ctrl_u() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('u'))),
KeyAction::Clear
);
}
#[test]
fn test_handle_ctrl_unknown() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event_ctrl(KeyCode::Char('x'))),
KeyAction::None
);
}
#[test]
fn test_handle_unknown_key() {
let handler = InputHandler::new();
assert_eq!(
handler.handle_key(key_event(KeyCode::F(1))),
KeyAction::None
);
}
#[test]
fn test_handle_tab() {
let handler = InputHandler::new();
assert_eq!(handler.handle_key(key_event(KeyCode::Tab)), KeyAction::None);
}
#[test]
fn test_is_valid_char_digits() {
for c in '0'..='9' {
assert!(InputHandler::is_valid_char(c), "Digit {c} should be valid");
}
}
#[test]
fn test_is_valid_char_operators() {
let valid = ['+', '-', '*', '/', '%', '^', '(', ')', '.', ' '];
for c in valid {
assert!(InputHandler::is_valid_char(c), "Char '{c}' should be valid");
}
}
#[test]
fn test_is_valid_char_invalid() {
let invalid = ['a', 'z', 'A', 'Z', '@', '#', '$', '!', '&', '|'];
for c in invalid {
assert!(
!InputHandler::is_valid_char(c),
"Char '{c}' should be invalid"
);
}
}
#[test]
fn test_key_action_debug() {
let action = KeyAction::Evaluate;
assert!(format!("{:?}", action).contains("Evaluate"));
}
#[test]
fn test_key_action_clone() {
let action = KeyAction::InsertChar('x');
let cloned = action;
assert_eq!(action, cloned);
}
#[test]
fn test_key_action_copy() {
let action = KeyAction::Quit;
let copied: KeyAction = action;
assert_eq!(action, copied);
}
}