requestty_ui/events/
mod.rs

1//! A module for handling key events
2
3use std::io;
4
5#[cfg(feature = "crossterm")]
6mod crossterm;
7#[cfg(feature = "termion")]
8mod termion;
9
10#[cfg(feature = "crossterm")]
11pub use self::crossterm::CrosstermEvents;
12
13#[cfg(feature = "termion")]
14pub use self::termion::TermionEvents;
15
16mod keys;
17mod movement;
18
19pub use keys::{KeyCode, KeyEvent, KeyModifiers};
20pub use movement::Movement;
21
22/// Gets the default [`EventIterator`] based on the features enabled.
23#[cfg(any(feature = "crossterm", feature = "termion"))]
24#[cfg_attr(docsrs, doc(cfg(any(feature = "crossterm", feature = "termion"))))]
25pub fn get_events() -> impl EventIterator {
26    #[cfg(feature = "crossterm")]
27    return CrosstermEvents::new();
28
29    // XXX: Only works when crossterm and termion are the only two available backends
30    //
31    // Instead of directly checking for termion, we check for not crossterm so that compiling
32    // (documentation) with both features enabled will not error
33    #[cfg(not(feature = "crossterm"))]
34    return TermionEvents::new();
35}
36
37/// A trait to represent a source of [`KeyEvent`]s.
38pub trait EventIterator {
39    /// Get the next event
40    fn next_event(&mut self) -> io::Result<KeyEvent>;
41}
42
43/// A simple wrapper around a [`KeyEvent`] iterator that can be used in tests.
44///
45/// Even though [`EventIterator`] expects the iterator to be infinite, only having enough events to
46/// complete the test is necessary.
47///
48/// It will also check that the internal iterator is fully exhausted on [`Drop`].
49///
50/// # Panics
51///
52/// It will panic if the events run out [`next_event`] is called, or if there are events remaining
53/// when dropped.
54///
55/// [`next_event`]: TestEvents::next_event
56#[derive(Debug, Clone)]
57pub struct TestEvents<E: Iterator<Item = KeyEvent>> {
58    events: E,
59}
60
61impl<E: Iterator<Item = KeyEvent>> TestEvents<E> {
62    /// Create a new `TestEvents`
63    pub fn new<I: IntoIterator<IntoIter = E, Item = KeyEvent>>(iter: I) -> Self {
64        Self {
65            events: iter.into_iter(),
66        }
67    }
68}
69
70impl TestEvents<std::iter::Empty<KeyEvent>> {
71    /// Create a new `TestEvents` which yields no events
72    pub fn empty() -> Self {
73        Self {
74            events: std::iter::empty(),
75        }
76    }
77}
78
79impl<E: Iterator<Item = KeyEvent>> EventIterator for TestEvents<E> {
80    fn next_event(&mut self) -> io::Result<KeyEvent> {
81        Ok(self
82            .events
83            .next()
84            .expect("Events ran out, but another one was requested"))
85    }
86}
87
88impl<E: Iterator<Item = KeyEvent>> Drop for TestEvents<E> {
89    fn drop(&mut self) {
90        let mut count = 0;
91
92        while self.events.next().is_some() {
93            count += 1
94        }
95
96        assert_eq!(
97            count, 0,
98            "Events did not fully run out, {} events have not been consumed",
99            count
100        );
101    }
102}