use std::io::IsTerminal;
use std::time::Duration;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyAction {
Quit,
Pause,
None,
}
pub fn poll_key_action() -> KeyAction {
if !std::io::stdin().is_terminal() {
return KeyAction::None;
}
let timeout = Duration::from_millis(10);
match event::poll(timeout) {
Ok(true) => match event::read() {
Ok(Event::Key(key_event)) => map_key_event(key_event),
_ => KeyAction::None,
},
_ => KeyAction::None,
}
}
fn map_key_event(event: KeyEvent) -> KeyAction {
match event.code {
KeyCode::Char('q') | KeyCode::Char('Q') => KeyAction::Quit,
KeyCode::Char('c') if event.modifiers.contains(KeyModifiers::CONTROL) => KeyAction::Quit,
KeyCode::Esc => KeyAction::Quit,
KeyCode::Char('p') | KeyCode::Char('P') | KeyCode::Char(' ') => KeyAction::Pause,
_ => KeyAction::None,
}
}
pub fn should_quit() -> bool {
poll_key_action() == KeyAction::Quit
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_action_variants() {
assert_ne!(KeyAction::Quit, KeyAction::None);
assert_ne!(KeyAction::Pause, KeyAction::None);
assert_ne!(KeyAction::Quit, KeyAction::Pause);
}
#[test]
fn poll_returns_none_in_non_tty() {
let action = poll_key_action();
assert_eq!(action, KeyAction::None);
}
#[test]
fn map_quit_keys() {
use crossterm::event::KeyEventKind;
let q_event = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
assert_eq!(map_key_event(q_event), KeyAction::Quit);
let esc_event = KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE);
assert_eq!(map_key_event(esc_event), KeyAction::Quit);
let ctrl_c = KeyEvent::new_with_kind(
KeyCode::Char('c'),
KeyModifiers::CONTROL,
KeyEventKind::Press,
);
assert_eq!(map_key_event(ctrl_c), KeyAction::Quit);
}
#[test]
fn map_pause_keys() {
let p_event = KeyEvent::new(KeyCode::Char('p'), KeyModifiers::NONE);
assert_eq!(map_key_event(p_event), KeyAction::Pause);
let space_event = KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE);
assert_eq!(map_key_event(space_event), KeyAction::Pause);
}
#[test]
fn map_unknown_keys() {
let a_event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE);
assert_eq!(map_key_event(a_event), KeyAction::None);
}
}