system_harness/
lib.rs

1#![doc = include_str!("../README.md")]
2//!
3//! # QEMU
4//! 
5//! A [`QemuSystem`](`crate::QemuSystem`) that implements 
6//! [`SystemHarness`](`crate::SystemHarness`) can be instantiated using a 
7//! [`QemuSystemConfig`](`crate::QemuSystemConfig`) that can be deserialized
8//! using serde.
9//!
10//! The top-most mapping should align with the QEMU argument names with the
11//! sub-mappings aligning with the backends and/or properties of the arguments.
12//!
13//! An example QEMU configuration:
14//!```json
15#![doc = include_str!("../tests/data/qemu-config.json")]
16//!```
17//! # Containers
18//!
19//! A [`ContainerSystem`](`crate::ContainerSystem`) that implements 
20//! [`SystemHarness`](`crate::SystemHarness`) can be instantiated using a 
21//! [`ContainerSystemConfig`](`crate::ContainerSystemConfig`) that can be deserialized
22//! using serde.
23//!
24//! An example of a container configuration:
25//!```json
26#![doc = include_str!("../tests/data/container-config.json")]
27//!```
28use std::time::SystemTime;
29use std::future::Future;
30use tokio::io::AsyncWrite;
31use tokio::io::AsyncRead;
32
33/// System keyboard key
34#[derive(Debug, PartialEq)]
35pub enum Key {
36    Enter,
37    Backspace,
38    Char {
39        chr: char
40    }
41}
42
43/// System status
44#[derive(Debug, PartialEq)]
45pub enum Status {
46    Running,
47    Paused,
48    Suspended,
49    Shutdown,
50}
51
52/// Type of event
53#[derive(Debug, PartialEq)]
54pub enum EventKind {
55    Shutdown,
56    Resume,
57    Pause,
58    Suspend,
59}
60
61/// A machine event
62pub struct Event {
63    /// Type of event
64    pub kind: EventKind,
65
66    /// Time event occurred
67    pub timestamp: SystemTime,
68}
69
70/// A trait representing event listener
71pub trait EventSubscriber: Send + Sync + 'static {
72    /// Action to be performed on event
73    fn on_event(&mut self, event: &Event);
74}
75
76/// A trait representing a harnessed system
77pub trait SystemHarness<'sys> {
78
79    type Terminal: SystemTerminal;
80
81    /// Get a terminal for the system 
82    fn terminal(&'sys mut self) -> impl Future<Output = Result<Self::Terminal, Error>>;
83
84    /// Pause system
85    fn pause(&mut self) -> impl Future<Output = Result<(), Error>>;
86
87    /// Resume system
88    fn resume(&mut self) -> impl Future<Output = Result<(), Error>>;
89
90    /// Shutdown system
91    fn shutdown(&mut self) -> impl Future<Output = Result<(), Error>>;
92
93    /// Get system status
94    fn status(&mut self) -> impl Future<Output = Result<Status, Error>>;
95
96    /// Check if harness is running
97    fn running(&mut self) -> impl Future<Output = Result<bool, Error>>;
98}
99
100/// A traint representing a system configuration
101pub trait SystemConfig<'sys> {
102
103    type System: SystemHarness<'sys>;
104
105    /// Spawn a system
106    fn spawn(&self) -> impl Future<Output = Result<Self::System, Error>>;
107
108}
109
110/// A trait representing a harnessed system that should be
111/// treated as a terminal
112pub trait SystemTerminal: AsyncWrite + AsyncRead + Unpin {
113
114    /// Send key to emulator
115    fn send_key(&mut self, key: Key) -> impl Future<Output = Result<(), Error>>;
116
117    /// Send a command to the terminal
118    fn send_command(&mut self, command: &str) -> impl Future<Output = Result<(), Error>> {
119        async move {
120            for chr in command.chars() {
121                self.send_key(Key::Char { chr }).await?;
122            }
123            self.send_key(Key::Enter).await
124        }
125    }
126
127}
128
129/// An event publisher
130pub trait EventPublisher {
131    /// Subscribe event listener
132    fn subscribe(&mut self, subscriber: impl EventSubscriber) -> Result<(), Error>;
133}
134
135impl<F> EventSubscriber for F
136where
137    F: FnMut(&Event) + Send + Sync + 'static,
138{
139    fn on_event(&mut self, event: &Event) {
140        (self)(event)
141    }
142}
143
144mod error;
145pub use error::Error;
146pub use error::ErrorKind;
147
148#[cfg(all(target_family = "unix", feature = "container"))]
149mod container;
150#[cfg(all(target_family = "unix", feature = "container"))]
151pub use container::*;
152
153#[cfg(all(target_family = "unix", feature = "qemu"))]
154mod qemu;
155#[cfg(all(target_family = "unix", feature = "qemu"))]
156pub use qemu::*;
157
158#[cfg(test)]
159mod tests {
160
161    use super::*;
162
163    struct FakeEventPublisher(Vec<Box<dyn EventSubscriber>>);
164
165    impl FakeEventPublisher {
166        pub fn publish(&mut self) {
167            let event = Event {
168                kind: EventKind::Shutdown,
169                timestamp: SystemTime::now(),
170            };
171            for subscriber in &mut self.0 {
172                subscriber.on_event(&event);
173            }
174        }
175    }
176
177    impl EventPublisher for FakeEventPublisher {
178        fn subscribe(&mut self, subscriber: impl EventSubscriber) -> Result<(), Error> {
179            self.0.push(Box::new(subscriber));
180            Ok(())
181        }
182    }
183
184    #[test]
185    fn fn_subscribe() {
186        let mut publisher = FakeEventPublisher(Vec::new());
187        publisher.subscribe(|_event: &Event| {}).unwrap();
188        publisher.publish();
189    }
190}