system-harness 1.0.0

An system harness abstraction and configuration serialization provider for virtualization and emulation systems
Documentation
#![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::time::SystemTime;
use std::future::Future;
use tokio::io::AsyncWrite;
use tokio::io::AsyncRead;

/// System keyboard key
#[derive(Debug, PartialEq)]
pub enum Key {
    Enter,
    Backspace,
    Char {
        chr: char
    }
}

/// 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<'sys> {

    type Terminal: SystemTerminal;

    /// Get a terminal for the system 
    fn terminal(&'sys mut self) -> impl Future<Output = Result<Self::Terminal, Error>>;

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

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

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

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

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

/// A traint representing a system configuration
pub trait SystemConfig<'sys> {

    type System: SystemHarness<'sys>;

    /// Spawn a system
    fn spawn(&self) -> impl Future<Output = Result<Self::System, Error>>;

}

/// A trait representing a harnessed system that should be
/// treated as a terminal
pub trait SystemTerminal: AsyncWrite + AsyncRead + Unpin {

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

    /// Send a command to the terminal
    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
        }
    }

}

/// 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();
    }
}