use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyCode {
Char(char),
Enter,
Esc,
Tab,
Backspace,
Delete,
Insert,
Up,
Down,
Left,
Right,
Home,
End,
PageUp,
PageDown,
F(u8),
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Modifiers: u8 {
const SHIFT = 0b0001;
const CTRL = 0b0010;
const ALT = 0b0100;
const META = 0b1000;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: Modifiers,
}
impl KeyEvent {
pub fn new(code: KeyCode) -> Self {
Self {
code,
modifiers: Modifiers::empty(),
}
}
pub fn with_modifiers(code: KeyCode, modifiers: Modifiers) -> Self {
Self { code, modifiers }
}
pub fn to_bytes(&self) -> Vec<u8> {
encode_key_event(self)
}
}
pub fn encode_key_event(event: &KeyEvent) -> Vec<u8> {
if event.modifiers.contains(Modifiers::CTRL) {
if let KeyCode::Char(c) = event.code {
return encode_ctrl_char(c);
}
}
if event.modifiers.contains(Modifiers::ALT) {
if let KeyCode::Char(c) = event.code {
let mut bytes = vec![0x1b]; bytes.extend_from_slice(c.to_string().as_bytes());
return bytes;
}
}
match event.code {
KeyCode::Char(c) => c.to_string().into_bytes(),
KeyCode::Enter => b"\n".to_vec(),
KeyCode::Tab => b"\t".to_vec(),
KeyCode::Esc => vec![0x1b],
KeyCode::Backspace => vec![0x7f], KeyCode::Delete => b"\x1b[3~".to_vec(),
KeyCode::Insert => b"\x1b[2~".to_vec(),
KeyCode::Up => b"\x1b[A".to_vec(),
KeyCode::Down => b"\x1b[B".to_vec(),
KeyCode::Right => b"\x1b[C".to_vec(),
KeyCode::Left => b"\x1b[D".to_vec(),
KeyCode::Home => b"\x1b[H".to_vec(),
KeyCode::End => b"\x1b[F".to_vec(),
KeyCode::PageUp => b"\x1b[5~".to_vec(),
KeyCode::PageDown => b"\x1b[6~".to_vec(),
KeyCode::F(n) => encode_function_key(n),
}
}
fn encode_ctrl_char(c: char) -> Vec<u8> {
let c_upper = c.to_ascii_uppercase();
let byte = match c_upper {
'A'..='Z' => (c_upper as u8) - b'A' + 1,
'@' => 0, '[' => 27, '\\' => 28, ']' => 29, '^' => 30, '_' => 31, '?' => 127,
'a'..='z' => (c_upper as u8) - b'A' + 1,
_ => {
return c.to_string().into_bytes();
}
};
vec![byte]
}
fn encode_function_key(n: u8) -> Vec<u8> {
match n {
1 => b"\x1bOP".to_vec(),
2 => b"\x1bOQ".to_vec(),
3 => b"\x1bOR".to_vec(),
4 => b"\x1bOS".to_vec(),
5 => b"\x1b[15~".to_vec(),
6 => b"\x1b[17~".to_vec(),
7 => b"\x1b[18~".to_vec(),
8 => b"\x1b[19~".to_vec(),
9 => b"\x1b[20~".to_vec(),
10 => b"\x1b[21~".to_vec(),
11 => b"\x1b[23~".to_vec(),
12 => b"\x1b[24~".to_vec(),
_ => Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_event_new() {
let event = KeyEvent::new(KeyCode::Char('a'));
assert_eq!(event.code, KeyCode::Char('a'));
assert_eq!(event.modifiers, Modifiers::empty());
}
#[test]
fn test_key_event_with_modifiers() {
let event = KeyEvent::with_modifiers(KeyCode::Char('c'), Modifiers::CTRL);
assert_eq!(event.code, KeyCode::Char('c'));
assert_eq!(event.modifiers, Modifiers::CTRL);
}
#[test]
fn test_encode_simple_char() {
let event = KeyEvent::new(KeyCode::Char('a'));
assert_eq!(event.to_bytes(), b"a");
let event = KeyEvent::new(KeyCode::Char('Z'));
assert_eq!(event.to_bytes(), b"Z");
let event = KeyEvent::new(KeyCode::Char('5'));
assert_eq!(event.to_bytes(), b"5");
}
#[test]
fn test_encode_special_chars() {
let event = KeyEvent::new(KeyCode::Enter);
assert_eq!(event.to_bytes(), b"\n");
let event = KeyEvent::new(KeyCode::Tab);
assert_eq!(event.to_bytes(), b"\t");
let event = KeyEvent::new(KeyCode::Esc);
assert_eq!(event.to_bytes(), vec![0x1b]);
let event = KeyEvent::new(KeyCode::Backspace);
assert_eq!(event.to_bytes(), vec![0x7f]);
}
#[test]
fn test_encode_navigation_keys() {
let event = KeyEvent::new(KeyCode::Up);
assert_eq!(event.to_bytes(), b"\x1b[A");
let event = KeyEvent::new(KeyCode::Down);
assert_eq!(event.to_bytes(), b"\x1b[B");
let event = KeyEvent::new(KeyCode::Right);
assert_eq!(event.to_bytes(), b"\x1b[C");
let event = KeyEvent::new(KeyCode::Left);
assert_eq!(event.to_bytes(), b"\x1b[D");
let event = KeyEvent::new(KeyCode::Home);
assert_eq!(event.to_bytes(), b"\x1b[H");
let event = KeyEvent::new(KeyCode::End);
assert_eq!(event.to_bytes(), b"\x1b[F");
}
#[test]
fn test_encode_page_keys() {
let event = KeyEvent::new(KeyCode::PageUp);
assert_eq!(event.to_bytes(), b"\x1b[5~");
let event = KeyEvent::new(KeyCode::PageDown);
assert_eq!(event.to_bytes(), b"\x1b[6~");
}
#[test]
fn test_encode_delete_insert() {
let event = KeyEvent::new(KeyCode::Delete);
assert_eq!(event.to_bytes(), b"\x1b[3~");
let event = KeyEvent::new(KeyCode::Insert);
assert_eq!(event.to_bytes(), b"\x1b[2~");
}
#[test]
fn test_encode_function_keys() {
let event = KeyEvent::new(KeyCode::F(1));
assert_eq!(event.to_bytes(), b"\x1bOP");
let event = KeyEvent::new(KeyCode::F(2));
assert_eq!(event.to_bytes(), b"\x1bOQ");
let event = KeyEvent::new(KeyCode::F(3));
assert_eq!(event.to_bytes(), b"\x1bOR");
let event = KeyEvent::new(KeyCode::F(4));
assert_eq!(event.to_bytes(), b"\x1bOS");
let event = KeyEvent::new(KeyCode::F(5));
assert_eq!(event.to_bytes(), b"\x1b[15~");
let event = KeyEvent::new(KeyCode::F(12));
assert_eq!(event.to_bytes(), b"\x1b[24~");
}
#[test]
fn test_encode_ctrl_combinations() {
let event = KeyEvent::with_modifiers(KeyCode::Char('a'), Modifiers::CTRL);
assert_eq!(event.to_bytes(), vec![1]);
let event = KeyEvent::with_modifiers(KeyCode::Char('c'), Modifiers::CTRL);
assert_eq!(event.to_bytes(), vec![3]);
let event = KeyEvent::with_modifiers(KeyCode::Char('d'), Modifiers::CTRL);
assert_eq!(event.to_bytes(), vec![4]);
let event = KeyEvent::with_modifiers(KeyCode::Char('z'), Modifiers::CTRL);
assert_eq!(event.to_bytes(), vec![26]);
let event = KeyEvent::with_modifiers(KeyCode::Char('['), Modifiers::CTRL);
assert_eq!(event.to_bytes(), vec![27]);
}
#[test]
fn test_encode_ctrl_uppercase() {
let event_lower = KeyEvent::with_modifiers(KeyCode::Char('a'), Modifiers::CTRL);
let event_upper = KeyEvent::with_modifiers(KeyCode::Char('A'), Modifiers::CTRL);
assert_eq!(event_lower.to_bytes(), event_upper.to_bytes());
}
#[test]
fn test_encode_alt_combinations() {
let event = KeyEvent::with_modifiers(KeyCode::Char('a'), Modifiers::ALT);
assert_eq!(event.to_bytes(), b"\x1ba");
let event = KeyEvent::with_modifiers(KeyCode::Char('x'), Modifiers::ALT);
assert_eq!(event.to_bytes(), b"\x1bx");
}
#[test]
fn test_modifier_combinations() {
let ctrl = Modifiers::CTRL;
let shift = Modifiers::SHIFT;
let alt = Modifiers::ALT;
let ctrl_shift = ctrl | shift;
assert!(ctrl_shift.contains(Modifiers::CTRL));
assert!(ctrl_shift.contains(Modifiers::SHIFT));
assert!(!ctrl_shift.contains(Modifiers::ALT));
let ctrl_alt = ctrl | alt;
assert!(ctrl_alt.contains(Modifiers::CTRL));
assert!(ctrl_alt.contains(Modifiers::ALT));
}
#[test]
fn test_keycode_equality() {
assert_eq!(KeyCode::Char('a'), KeyCode::Char('a'));
assert_ne!(KeyCode::Char('a'), KeyCode::Char('b'));
assert_eq!(KeyCode::Enter, KeyCode::Enter);
assert_eq!(KeyCode::F(1), KeyCode::F(1));
assert_ne!(KeyCode::F(1), KeyCode::F(2));
}
#[test]
fn test_key_event_equality() {
let event1 = KeyEvent::new(KeyCode::Char('a'));
let event2 = KeyEvent::new(KeyCode::Char('a'));
assert_eq!(event1, event2);
let event3 = KeyEvent::with_modifiers(KeyCode::Char('a'), Modifiers::CTRL);
assert_ne!(event1, event3);
}
}