#![doc = include_str!("../README.md")]
#![doc = include_str!("../tests/data/qemu-config.json")]
#![doc = include_str!("../tests/data/container-config.json")]
use std::time::SystemTime;
use std::future::Future;
use tokio::io::AsyncWrite;
use tokio::io::AsyncRead;
#[derive(Debug, PartialEq)]
pub enum Key {
Enter,
Backspace,
Char {
chr: char
}
}
#[derive(Debug, PartialEq)]
pub enum Status {
Running,
Paused,
Suspended,
Shutdown,
}
#[derive(Debug, PartialEq)]
pub enum EventKind {
Shutdown,
Resume,
Pause,
Suspend,
}
pub struct Event {
pub kind: EventKind,
pub timestamp: SystemTime,
}
pub trait EventSubscriber: Send + Sync + 'static {
fn on_event(&mut self, event: &Event);
}
pub trait SystemHarness<'sys> {
type Terminal: SystemTerminal;
fn terminal(&'sys mut self) -> impl Future<Output = Result<Self::Terminal, Error>>;
fn pause(&mut self) -> impl Future<Output = Result<(), Error>>;
fn resume(&mut self) -> impl Future<Output = Result<(), Error>>;
fn shutdown(&mut self) -> impl Future<Output = Result<(), Error>>;
fn status(&mut self) -> impl Future<Output = Result<Status, Error>>;
fn running(&mut self) -> impl Future<Output = Result<bool, Error>>;
}
pub trait SystemConfig<'sys> {
type System: SystemHarness<'sys>;
fn spawn(&self) -> impl Future<Output = Result<Self::System, Error>>;
}
pub trait SystemTerminal: AsyncWrite + AsyncRead + Unpin {
fn send_key(&mut self, key: Key) -> impl Future<Output = Result<(), Error>>;
fn send_command(&mut self, command: &str) -> impl Future<Output = Result<(), Error>> {
async move {
for chr in command.chars() {
self.send_key(Key::Char { chr }).await?;
}
self.send_key(Key::Enter).await
}
}
}
pub trait EventPublisher {
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();
}
}