input/
event_provider.rs

1use std::time::Duration;
2
3use anyhow::{anyhow, Result};
4#[cfg(not(test))]
5use crossterm::event::{poll, read};
6use crossterm::event::{Event, KeyEvent, KeyEventKind, MouseEvent, MouseEventKind};
7#[cfg(test)]
8use read_event_mocks::{poll, read};
9
10/// Function that returns a event
11pub trait EventReaderFn: Fn() -> Result<Option<Event>> + Send + Sync + 'static {}
12
13impl<FN: Fn() -> Result<Option<Event>> + Send + Sync + 'static> EventReaderFn for FN {}
14
15/// Read the next input event from the terminal interface.
16///
17/// # Errors
18///
19/// Errors if the Tui cannot read an event for any reason. In general this should not error, and
20/// if this does generate an error, the Tui should be considered to be in a non-recoverable
21/// state.
22#[inline]
23pub fn read_event() -> Result<Option<Event>> {
24	if poll(Duration::from_millis(20)).unwrap_or(false) {
25		read()
26			.map(|event| {
27				match event {
28					e @ (Event::Key(KeyEvent {
29						kind: KeyEventKind::Press | KeyEventKind::Repeat,
30						..
31					})
32					| Event::Mouse(MouseEvent {
33						kind: MouseEventKind::Down(_) | MouseEventKind::ScrollDown | MouseEventKind::ScrollUp,
34						..
35					})
36					| Event::Resize(..)) => Some(e),
37					Event::Key(_) | Event::Mouse(_) | Event::Paste(_) | Event::FocusGained | Event::FocusLost => None,
38				}
39			})
40			.map_err(|err| anyhow!("{:#}", err).context("Unexpected Error"))
41	}
42	else {
43		Ok(None)
44	}
45}
46
47#[cfg(test)]
48mod read_event_mocks {
49	use std::{mem, time::Duration};
50
51	use crossterm::{
52		event::{Event, KeyCode, KeyEvent},
53		Result,
54	};
55	use lazy_static::lazy_static;
56	use parking_lot::Mutex;
57
58	lazy_static! {
59		pub static ref HAS_POLLED_EVENT: Mutex<Result<bool>> = Mutex::new(Ok(true));
60		pub static ref NEXT_EVENT: Mutex<Result<Event>> = Mutex::new(Ok(Event::Key(KeyEvent::from(KeyCode::Null))));
61	}
62
63	pub(crate) fn poll(_: Duration) -> Result<bool> {
64		let mut lock = HAS_POLLED_EVENT.lock();
65		mem::replace(&mut *lock, Ok(false))
66	}
67
68	pub(crate) fn read() -> Result<Event> {
69		let mut lock = NEXT_EVENT.lock();
70		mem::replace(&mut *lock, Ok(Event::Key(KeyEvent::from(KeyCode::Null))))
71	}
72}
73
74#[cfg(test)]
75mod tests {
76	use std::{io, io::ErrorKind};
77
78	use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton};
79
80	use super::*;
81
82	#[test]
83	#[serial_test::serial]
84	fn read_event_poll_error() {
85		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
86		*lock = Err(io::Error::from(ErrorKind::Other));
87		drop(lock);
88
89		assert!(read_event().unwrap().is_none());
90	}
91
92	#[test]
93	#[serial_test::serial]
94	fn read_event_poll_timeout() {
95		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
96		*lock = Ok(false);
97		drop(lock);
98
99		assert!(read_event().unwrap().is_none());
100	}
101
102	#[test]
103	#[serial_test::serial]
104	fn read_event_read_error() {
105		let mut lock = read_event_mocks::NEXT_EVENT.lock();
106		*lock = Err(io::Error::from(ErrorKind::Other));
107		drop(lock);
108
109		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
110		*lock = Ok(true);
111		drop(lock);
112
113		assert_eq!(read_event().unwrap_err().to_string(), "Unexpected Error");
114	}
115
116	#[test]
117	#[serial_test::serial]
118	fn read_event_read_key_press() {
119		let mut lock = read_event_mocks::NEXT_EVENT.lock();
120		*lock = Ok(Event::Key(KeyEvent::from(KeyCode::Enter)));
121		drop(lock);
122
123		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
124		*lock = Ok(true);
125		drop(lock);
126
127		assert_eq!(read_event().unwrap(), Some(Event::Key(KeyEvent::from(KeyCode::Enter))));
128	}
129
130	#[test]
131	#[serial_test::serial]
132	fn read_event_read_key_repeat() {
133		let mut lock = read_event_mocks::NEXT_EVENT.lock();
134		*lock = Ok(Event::Key(KeyEvent::new_with_kind(
135			KeyCode::Enter,
136			KeyModifiers::NONE,
137			KeyEventKind::Repeat,
138		)));
139		drop(lock);
140
141		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
142		*lock = Ok(true);
143		drop(lock);
144
145		assert_eq!(
146			read_event().unwrap(),
147			Some(Event::Key(KeyEvent::new_with_kind(
148				KeyCode::Enter,
149				KeyModifiers::NONE,
150				KeyEventKind::Repeat,
151			)))
152		);
153	}
154
155	#[test]
156	#[serial_test::serial]
157	fn read_event_read_mouse_down() {
158		let mut lock = read_event_mocks::NEXT_EVENT.lock();
159		*lock = Ok(Event::Mouse(MouseEvent {
160			kind: MouseEventKind::Down(MouseButton::Right),
161			column: 0,
162			row: 0,
163			modifiers: KeyModifiers::NONE,
164		}));
165		drop(lock);
166
167		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
168		*lock = Ok(true);
169		drop(lock);
170
171		assert_eq!(
172			read_event().unwrap(),
173			Some(Event::Mouse(MouseEvent {
174				kind: MouseEventKind::Down(MouseButton::Right),
175				column: 0,
176				row: 0,
177				modifiers: KeyModifiers::NONE
178			}))
179		);
180	}
181
182	#[test]
183	#[serial_test::serial]
184	fn read_event_read_mouse_scroll_down() {
185		let mut lock = read_event_mocks::NEXT_EVENT.lock();
186		*lock = Ok(Event::Mouse(MouseEvent {
187			kind: MouseEventKind::ScrollDown,
188			column: 0,
189			row: 0,
190			modifiers: KeyModifiers::NONE,
191		}));
192		drop(lock);
193
194		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
195		*lock = Ok(true);
196		drop(lock);
197
198		assert_eq!(
199			read_event().unwrap(),
200			Some(Event::Mouse(MouseEvent {
201				kind: MouseEventKind::ScrollDown,
202				column: 0,
203				row: 0,
204				modifiers: KeyModifiers::NONE
205			}))
206		);
207	}
208
209	#[test]
210	#[serial_test::serial]
211	fn read_event_read_mouse_scroll_up() {
212		let mut lock = read_event_mocks::NEXT_EVENT.lock();
213		*lock = Ok(Event::Mouse(MouseEvent {
214			kind: MouseEventKind::ScrollDown,
215			column: 0,
216			row: 0,
217			modifiers: KeyModifiers::NONE,
218		}));
219		drop(lock);
220
221		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
222		*lock = Ok(true);
223		drop(lock);
224
225		assert_eq!(
226			read_event().unwrap(),
227			Some(Event::Mouse(MouseEvent {
228				kind: MouseEventKind::ScrollDown,
229				column: 0,
230				row: 0,
231				modifiers: KeyModifiers::NONE
232			}))
233		);
234	}
235
236	#[test]
237	#[serial_test::serial]
238	fn read_event_read_resize() {
239		let mut lock = read_event_mocks::NEXT_EVENT.lock();
240		*lock = Ok(Event::Resize(1, 1));
241		drop(lock);
242
243		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
244		*lock = Ok(true);
245		drop(lock);
246
247		assert_eq!(read_event().unwrap(), Some(Event::Resize(1, 1)));
248	}
249
250	#[test]
251	#[serial_test::serial]
252	fn read_event_read_key_other() {
253		let mut lock = read_event_mocks::NEXT_EVENT.lock();
254		*lock = Ok(Event::Key(KeyEvent::new_with_kind(
255			KeyCode::Enter,
256			KeyModifiers::NONE,
257			KeyEventKind::Release,
258		)));
259		drop(lock);
260
261		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
262		*lock = Ok(true);
263		drop(lock);
264
265		assert_eq!(read_event().unwrap(), None);
266	}
267
268	#[test]
269	#[serial_test::serial]
270	fn read_event_read_mouse_other() {
271		let mut lock = read_event_mocks::NEXT_EVENT.lock();
272		*lock = Ok(Event::Mouse(MouseEvent {
273			kind: MouseEventKind::Moved,
274			column: 0,
275			row: 0,
276			modifiers: KeyModifiers::NONE,
277		}));
278		drop(lock);
279
280		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
281		*lock = Ok(true);
282		drop(lock);
283
284		assert_eq!(read_event().unwrap(), None);
285	}
286
287	#[test]
288	#[serial_test::serial]
289	fn read_event_read_paste() {
290		let mut lock = read_event_mocks::NEXT_EVENT.lock();
291		*lock = Ok(Event::Paste(String::from("Foo")));
292		drop(lock);
293
294		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
295		*lock = Ok(true);
296		drop(lock);
297
298		assert_eq!(read_event().unwrap(), None);
299	}
300
301	#[test]
302	#[serial_test::serial]
303	fn read_event_read_focus_gained() {
304		let mut lock = read_event_mocks::NEXT_EVENT.lock();
305		*lock = Ok(Event::FocusGained);
306		drop(lock);
307
308		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
309		*lock = Ok(true);
310		drop(lock);
311
312		assert_eq!(read_event().unwrap(), None);
313	}
314
315	#[test]
316	#[serial_test::serial]
317	fn read_event_read_focus_lost() {
318		let mut lock = read_event_mocks::NEXT_EVENT.lock();
319		*lock = Ok(Event::FocusLost);
320		drop(lock);
321
322		let mut lock = read_event_mocks::HAS_POLLED_EVENT.lock();
323		*lock = Ok(true);
324		drop(lock);
325
326		assert_eq!(read_event().unwrap(), None);
327	}
328}