thedes_tui_core/runtime/device/
mock.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicBool, Ordering::*},
4};
5
6use crate::{
7    geometry::CoordPair,
8    input::device::{InputDevice, mock::InputDeviceMock},
9    panic::restore::{PanicRestoreGuard, mock::PanicRestoreMock},
10    screen::device::{ScreenDevice, mock::ScreenDeviceMock},
11};
12
13use super::{Error, RuntimeDevice};
14
15#[derive(Debug)]
16struct Shared {
17    open: AtomicBool,
18    initialized: AtomicBool,
19}
20
21impl Shared {
22    pub fn new() -> Self {
23        Self {
24            open: AtomicBool::new(false),
25            initialized: AtomicBool::new(false),
26        }
27    }
28
29    pub fn initialized(&self) -> bool {
30        self.initialized.load(Acquire)
31    }
32
33    pub fn initialize(&self) -> bool {
34        !self.initialized.swap(true, AcqRel)
35    }
36
37    pub fn shutdown(&self) -> bool {
38        self.initialized.swap(false, AcqRel)
39    }
40
41    pub fn mark_device_open(&self) -> bool {
42        !self.open.swap(true, AcqRel)
43    }
44}
45
46#[derive(Debug, Clone)]
47pub struct RuntimeDeviceMock {
48    screen: ScreenDeviceMock,
49    input: InputDeviceMock,
50    panic_restore: PanicRestoreMock,
51    shared: Arc<Shared>,
52}
53
54impl RuntimeDeviceMock {
55    pub fn new(term_size: CoordPair) -> Self {
56        Self {
57            screen: ScreenDeviceMock::new(term_size),
58            input: InputDeviceMock::new(),
59            panic_restore: PanicRestoreMock::new(),
60            shared: Arc::new(Shared::new()),
61        }
62    }
63
64    pub fn initialized(&self) -> bool {
65        self.shared.initialized()
66    }
67
68    fn initialize(&self) -> bool {
69        self.shared.initialize()
70    }
71
72    fn shutdown(&self) -> bool {
73        self.shared.shutdown()
74    }
75
76    pub fn screen(&self) -> &ScreenDeviceMock {
77        &self.screen
78    }
79
80    pub fn input(&self) -> &InputDeviceMock {
81        &self.input
82    }
83
84    pub fn panic_restore(&self) -> &PanicRestoreMock {
85        &self.panic_restore
86    }
87
88    pub fn open(&self) -> Box<dyn RuntimeDevice> {
89        if !self.shared.mark_device_open() {
90            panic!("Mocked runtime device was already open for this mock");
91        }
92        Box::new(MockedRuntimeDevice::new(self.clone()))
93    }
94}
95
96#[derive(Debug)]
97struct MockedRuntimeDevice {
98    mock: RuntimeDeviceMock,
99}
100
101impl MockedRuntimeDevice {
102    pub fn new(mock: RuntimeDeviceMock) -> Self {
103        Self { mock }
104    }
105}
106
107impl RuntimeDevice for MockedRuntimeDevice {
108    fn blocking_init(&mut self) -> Result<(), Error> {
109        if self.mock.initialize() { Ok(()) } else { Err(Error::AlreadyInit) }
110    }
111
112    fn blocking_shutdown(&mut self) -> Result<(), Error> {
113        if self.mock.shutdown() { Ok(()) } else { Err(Error::NotInit) }
114    }
115
116    fn open_input_device(&mut self) -> Box<dyn InputDevice> {
117        self.mock.input().open()
118    }
119
120    fn open_screen_device(&mut self) -> Box<dyn ScreenDevice> {
121        self.mock.screen().open()
122    }
123
124    fn open_panic_restore_guard(&mut self) -> Box<dyn PanicRestoreGuard> {
125        self.mock.panic_restore().open()
126    }
127}