thedes_tui_core/input/device/
mock.rs1use std::{collections::VecDeque, mem, sync::Arc, time::Duration};
2
3use crate::event::InternalEvent;
4
5use super::{Error, InputDevice};
6
7#[derive(Debug)]
8struct State {
9 events: VecDeque<Result<InternalEvent, Error>>,
10 timeout_log: Option<Vec<Duration>>,
11 device_open: bool,
12}
13
14impl State {
15 pub fn new() -> Self {
16 Self { events: VecDeque::new(), timeout_log: None, device_open: false }
17 }
18
19 pub fn enable_timeout_log(&mut self) {
20 if self.timeout_log.is_none() {
21 self.timeout_log = Some(Vec::new());
22 }
23 }
24
25 pub fn disable_timeout_log(&mut self) -> Option<Vec<Duration>> {
26 self.timeout_log.take()
27 }
28
29 pub fn take_timeout_log(&mut self) -> Option<Vec<Duration>> {
30 self.timeout_log.as_mut().map(mem::take)
31 }
32
33 pub fn log_timeout(&mut self, duration: Duration) {
34 if let Some(log) = &mut self.timeout_log {
35 log.push(duration);
36 }
37 }
38
39 pub fn publish(
40 &mut self,
41 events: impl IntoIterator<Item = Result<InternalEvent, Error>>,
42 ) {
43 self.events.extend(events);
44 }
45
46 pub fn read_one(&mut self) -> Result<Option<InternalEvent>, Error> {
47 self.events.pop_front().transpose()
48 }
49
50 pub fn mark_device_open(&mut self) -> bool {
51 mem::replace(&mut self.device_open, true)
52 }
53}
54
55#[derive(Debug, Clone)]
56pub struct InputDeviceMock {
57 state: Arc<std::sync::Mutex<State>>,
58}
59
60impl InputDeviceMock {
61 pub fn new() -> Self {
62 Self { state: Arc::new(std::sync::Mutex::new(State::new())) }
63 }
64
65 pub fn open(&self) -> Box<dyn InputDevice> {
66 if self.with_state(|state| state.mark_device_open()) {
67 panic!("Mocked input device was already open for this mock");
68 }
69 Box::new(MockedInputDevice::new(self.clone()))
70 }
71
72 pub fn enable_timeout_log(&self) {
73 self.with_state(|state| state.enable_timeout_log())
74 }
75
76 pub fn disable_timeout_log(&self) -> Option<Vec<Duration>> {
77 self.with_state(|state| state.disable_timeout_log())
78 }
79
80 pub fn take_timeout_log(&self) -> Option<Vec<Duration>> {
81 self.with_state(|state| state.take_timeout_log())
82 }
83
84 pub fn publish(
85 &self,
86 events: impl IntoIterator<Item = Result<InternalEvent, Error>>,
87 ) {
88 self.with_state(|state| state.publish(events))
89 }
90
91 pub fn publish_ok<T>(&self, events: impl IntoIterator<Item = T>)
92 where
93 T: Into<InternalEvent>,
94 {
95 self.publish(events.into_iter().map(T::into).map(Ok));
96 }
97
98 pub fn publish_err(&self, events: impl IntoIterator<Item = Error>) {
99 self.publish(events.into_iter().map(Err));
100 }
101
102 fn read_one(
103 &mut self,
104 timeout: Duration,
105 ) -> Result<Option<InternalEvent>, Error> {
106 self.with_state(|state| {
107 state.log_timeout(timeout);
108 state.read_one()
109 })
110 }
111
112 fn with_state<F, T>(&self, scope: F) -> T
113 where
114 F: FnOnce(&mut State) -> T,
115 {
116 let mut state = self.state.lock().expect("poisoned lock");
117 scope(&mut state)
118 }
119}
120
121#[derive(Debug)]
122struct MockedInputDevice {
123 mock: InputDeviceMock,
124}
125
126impl MockedInputDevice {
127 pub fn new(mock: InputDeviceMock) -> Self {
128 Self { mock }
129 }
130}
131
132impl InputDevice for MockedInputDevice {
133 fn blocking_read(
134 &mut self,
135 timeout: std::time::Duration,
136 ) -> Result<Option<InternalEvent>, Error> {
137 self.mock.read_one(timeout)
138 }
139}