use std::time::Duration;
use anyhow::{anyhow, Result};
#[cfg(not(test))]
use crossterm::event::{poll, read};
use crossterm::event::{Event, KeyEvent, KeyEventKind, MouseEvent, MouseEventKind};
#[cfg(test)]
use read_event_mocks::{poll, read};
pub trait EventReaderFn: Fn() -> Result<Option<Event>> + Send + Sync + 'static {}
impl<FN: Fn() -> Result<Option<Event>> + Send + Sync + 'static> EventReaderFn for FN {}
#[inline]
pub fn read_event() -> Result<Option<Event>> {
if poll(Duration::from_millis(20)).unwrap_or(false) {
read()
.map(|event| {
match event {
e @ (Event::Key(KeyEvent {
kind: KeyEventKind::Press | KeyEventKind::Repeat,
..
})
| Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(_) | MouseEventKind::ScrollDown | MouseEventKind::ScrollUp,
..
})
| Event::Resize(..)) => Some(e),
Event::Key(_) | Event::Mouse(_) | Event::Paste(_) | Event::FocusGained | Event::FocusLost => None,
}
})
.map_err(|err| anyhow!("{:#}", err).context("Unexpected Error"))
}
else {
Ok(None)
}
}
#[cfg(test)]
mod read_event_mocks {
use std::{mem, time::Duration};
use crossterm::{
event::{Event, KeyCode, KeyEvent},
Result,
};
use lazy_static::lazy_static;
use parking_lot::Mutex;
lazy_static! {
pub static ref HAS_POLLED_EVENT: Mutex<Result<bool>> = Mutex::new(Ok(true));
pub static ref NEXT_EVENT: Mutex<Result<Event>> = Mutex::new(Ok(Event::Key(KeyEvent::from(KeyCode::Null))));
}
pub(crate) fn poll(_: Duration) -> Result<bool> {
let mut lock = HAS_POLLED_EVENT.lock();
mem::replace(&mut *lock, Ok(false))
}
pub(crate) fn read() -> Result<Event> {
let mut lock = NEXT_EVENT.lock();
mem::replace(&mut *lock, Ok(Event::Key(KeyEvent::from(KeyCode::Null))))
}
}
#[cfg(test)]
mod tests {
use std::{io, io::ErrorKind};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton};
use super::*;
#[test]
#[serial_test::serial]
fn read_event_poll_error() {
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Err(io::Error::from(ErrorKind::Other));
drop(lock);
assert!(read_event().unwrap().is_none());
}
#[test]
#[serial_test::serial]
fn read_event_poll_timeout() {
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(false);
drop(lock);
assert!(read_event().unwrap().is_none());
}
#[test]
#[serial_test::serial]
fn read_event_read_error() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Err(io::Error::from(ErrorKind::Other));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap_err().to_string(), "Unexpected Error");
}
#[test]
#[serial_test::serial]
fn read_event_read_key_press() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Key(KeyEvent::from(KeyCode::Enter)));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), Some(Event::Key(KeyEvent::from(KeyCode::Enter))));
}
#[test]
#[serial_test::serial]
fn read_event_read_key_repeat() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Key(KeyEvent::new_with_kind(
KeyCode::Enter,
KeyModifiers::NONE,
KeyEventKind::Repeat,
)));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(
read_event().unwrap(),
Some(Event::Key(KeyEvent::new_with_kind(
KeyCode::Enter,
KeyModifiers::NONE,
KeyEventKind::Repeat,
)))
);
}
#[test]
#[serial_test::serial]
fn read_event_read_mouse_down() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Right),
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(
read_event().unwrap(),
Some(Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Right),
column: 0,
row: 0,
modifiers: KeyModifiers::NONE
}))
);
}
#[test]
#[serial_test::serial]
fn read_event_read_mouse_scroll_down() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollDown,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(
read_event().unwrap(),
Some(Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollDown,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE
}))
);
}
#[test]
#[serial_test::serial]
fn read_event_read_mouse_scroll_up() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollDown,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(
read_event().unwrap(),
Some(Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollDown,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE
}))
);
}
#[test]
#[serial_test::serial]
fn read_event_read_resize() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Resize(1, 1));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), Some(Event::Resize(1, 1)));
}
#[test]
#[serial_test::serial]
fn read_event_read_key_other() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Key(KeyEvent::new_with_kind(
KeyCode::Enter,
KeyModifiers::NONE,
KeyEventKind::Release,
)));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), None);
}
#[test]
#[serial_test::serial]
fn read_event_read_mouse_other() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
column: 0,
row: 0,
modifiers: KeyModifiers::NONE,
}));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), None);
}
#[test]
#[serial_test::serial]
fn read_event_read_paste() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::Paste(String::from("Foo")));
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), None);
}
#[test]
#[serial_test::serial]
fn read_event_read_focus_gained() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::FocusGained);
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), None);
}
#[test]
#[serial_test::serial]
fn read_event_read_focus_lost() {
let mut lock = read_event_mocks::NEXT_EVENT.lock();
*lock = Ok(Event::FocusLost);
drop(lock);
let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
*lock = Ok(true);
drop(lock);
assert_eq!(read_event().unwrap(), None);
}
}