1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#![doc = include_str!("../README.md")]
//!
//! # QEMU
//! 
//! A [`QemuSystem`](`crate::QemuSystem`) that implements 
//! [`SystemHarness`](`crate::SystemHarness`) can be instantiated using a 
//! [`QemuSystemConfig`](`crate::QemuSystemConfig`) that can be deserialized
//! using serde.
//!
//! The top-most mapping should align with the QEMU argument names with the
//! sub-mappings aligning with the backends and/or properties of the arguments.
//!
//! An example QEMU configuration:
//!```json
#![doc = include_str!("../tests/data/qemu-config.json")]
//!```
//! # Containers
//!
//! A [`ContainerSystem`](`crate::ContainerSystem`) that implements 
//! [`SystemHarness`](`crate::SystemHarness`) can be instantiated using a 
//! [`ContainerSystemConfig`](`crate::ContainerSystemConfig`) that can be deserialized
//! using serde.
//!
//! An example of a container configuration:
//!```json
#![doc = include_str!("../tests/data/container-config.json")]
//!```
use std::io::{Read, Write};
use std::time::SystemTime;

/// System keyboard key
#[derive(Debug, PartialEq)]
pub enum Key {
    Enter,
}

/// System status
#[derive(Debug, PartialEq)]
pub enum Status {
    Running,
    Paused,
    Suspended,
    Shutdown,
}

/// Type of event
#[derive(Debug, PartialEq)]
pub enum EventKind {
    Shutdown,
    Resume,
    Pause,
    Suspend,
}

/// A machine event
pub struct Event {
    /// Type of event
    pub kind: EventKind,

    /// Time event occurred
    pub timestamp: SystemTime,
}

/// A trait representing event listener
pub trait EventSubscriber: Send + Sync + 'static {
    /// Action to be performed on event
    fn on_event(&mut self, event: &Event);
}

/// A trait representing a harnessed system
pub trait SystemHarness {

    type Terminal: SystemTerminal;

    /// Get a terminal for the system 
    fn terminal(&self) -> Result<Self::Terminal, Error>;

    /// Pause system
    fn pause(&mut self) -> Result<(), Error>;

    /// Resume system
    fn resume(&mut self) -> Result<(), Error>;

    /// Shutdown system
    fn shutdown(&mut self) -> Result<(), Error>;

    /// Get system status
    fn status(&mut self) -> Result<Status, Error>;

    /// Check if harness is running
    fn running(&mut self) -> Result<bool, Error>;
}

/// A trait representing a harnessed system that should be
/// treated as a terminal
pub trait SystemTerminal: Write + Read {

    /// Send key to emulator
    fn send_key(&mut self, key: Key) -> Result<(), Error>;

    /// Send a command to the terminal
    fn send_command(&mut self, command: &str) -> Result<(), Error> {
        self.write(command.as_bytes())?;
        self.flush()?;
        self.send_key(Key::Enter)
    }

}

/// An event publisher
pub trait EventPublisher {
    /// Subscribe event listener
    fn subscribe(&mut self, subscriber: impl EventSubscriber) -> Result<(), Error>;
}

impl<F> EventSubscriber for F
where
    F: FnMut(&Event) + Send + Sync + 'static,
{
    fn on_event(&mut self, event: &Event) {
        (self)(event)
    }
}

mod error;
pub use error::Error;
pub use error::ErrorKind;

#[cfg(all(target_family = "unix", feature = "container"))]
mod container;
#[cfg(all(target_family = "unix", feature = "container"))]
pub use container::*;

#[cfg(all(target_family = "unix", feature = "qemu"))]
mod qemu;
#[cfg(all(target_family = "unix", feature = "qemu"))]
pub use qemu::*;

#[cfg(test)]
mod tests {

    use super::*;

    struct FakeEventPublisher(Vec<Box<dyn EventSubscriber>>);

    impl FakeEventPublisher {
        pub fn publish(&mut self) {
            let event = Event {
                kind: EventKind::Shutdown,
                timestamp: SystemTime::now(),
            };
            for subscriber in &mut self.0 {
                subscriber.on_event(&event);
            }
        }
    }

    impl EventPublisher for FakeEventPublisher {
        fn subscribe(&mut self, subscriber: impl EventSubscriber) -> Result<(), Error> {
            self.0.push(Box::new(subscriber));
            Ok(())
        }
    }

    #[test]
    fn fn_subscribe() {
        let mut publisher = FakeEventPublisher(Vec::new());
        publisher.subscribe(|_event: &Event| {}).unwrap();
        publisher.publish();
    }
}