microde-application 0.4.0

Composition and lifecycle runtime for modular Microde applications.
Documentation
use std::sync::{Arc, Mutex};

use futures::executor::block_on;
use microde_application::{MicrodeError, MicrodeModule, ModuleFuture, ModuleKind};

struct PassiveTestModule {
    events: Arc<Mutex<Vec<&'static str>>>,
}

impl MicrodeModule for PassiveTestModule {
    const KIND: ModuleKind = ModuleKind::Passive;
    fn run(&mut self) -> ModuleFuture {
        let events = self.events.clone();
        Box::pin(async move {
            events.lock().unwrap().push("run");
            Ok(())
        })
    }

    fn stop(&mut self) -> ModuleFuture {
        let events = self.events.clone();
        Box::pin(async move {
            events.lock().unwrap().push("stop");
            Ok(())
        })
    }
}

struct ActiveTestModule {
    events: Arc<Mutex<Vec<&'static str>>>,
}

impl MicrodeModule for ActiveTestModule {
    const KIND: ModuleKind = ModuleKind::Active;
    fn run(&mut self) -> ModuleFuture {
        Box::pin(async { Err(MicrodeError::new("run failed")) })
    }
    fn stop(&mut self) -> ModuleFuture {
        let events = self.events.clone();
        Box::pin(async move {
            events.lock().unwrap().push("stop");
            Ok(())
        })
    }
}

struct NoOpModule;

impl MicrodeModule for NoOpModule {
    const KIND: ModuleKind = ModuleKind::Passive;
}

#[test]
fn run_and_stop_succeed_by_default() {
    let mut module = NoOpModule;

    block_on(module.run()).unwrap();
    block_on(module.stop()).unwrap();
}

#[test]
fn optional_lifecycle_phases_succeed_by_default() {
    let events = Arc::new(Mutex::new(Vec::new()));
    let mut module = PassiveTestModule {
        events: events.clone(),
    };

    block_on(module.initialize()).unwrap();
    block_on(module.setup()).unwrap();
    block_on(module.run()).unwrap();
    block_on(module.stop()).unwrap();
    block_on(module.teardown()).unwrap();
    block_on(module.shutdown()).unwrap();
    block_on(module.cleanup()).unwrap();

    assert_eq!(*events.lock().unwrap(), vec!["run", "stop"]);
}

#[test]
fn active_modules_can_override_the_stop_operation() {
    let events = Arc::new(Mutex::new(Vec::new()));
    let mut module = ActiveTestModule {
        events: events.clone(),
    };

    assert_eq!(
        block_on(module.run()).unwrap_err(),
        MicrodeError::new("run failed")
    );
    block_on(module.stop()).unwrap();

    assert_eq!(*events.lock().unwrap(), vec!["stop"]);
}

#[test]
fn an_active_run_can_remain_owned_while_stop_is_requested() {
    let events = Arc::new(Mutex::new(Vec::new()));
    let mut module = ActiveTestModule {
        events: events.clone(),
    };

    let run = module.run();
    let stop = module.stop();

    block_on(stop).unwrap();
    assert_eq!(block_on(run).unwrap_err(), MicrodeError::new("run failed"));
    assert_eq!(*events.lock().unwrap(), vec!["stop"]);
}