stateforward-sml 1.2.0

sml.rs: a no-std Rust implementation of the sml.cpp transition-table DSL
Documentation
use sml::sml;

pub struct Run {
    enabled: bool,
}

sml! {
    EvalDsl {
        *"idle"_s + event<Run> / (before, eval [enabled] / conditional, after) = X,
    }
}

#[derive(Default)]
struct Context {
    order: Vec<&'static str>,
}

impl EvalDslStateMachineContext for Context {
    fn enabled(&self, event: &Run) -> Result<bool, ()> {
        Ok(event.enabled)
    }

    fn before(&mut self, _event: &Run) -> Result<(), ()> {
        self.order.push("before");
        Ok(())
    }

    fn conditional(&mut self, _event: &Run) -> Result<(), ()> {
        self.order.push("conditional");
        Ok(())
    }

    fn after(&mut self, _event: &Run) -> Result<(), ()> {
        self.order.push("after");
        Ok(())
    }
}

#[test]
fn cpp_eval_action_preserves_sequence_order_and_guarding() {
    let mut enabled = EvalDslStateMachine::new(Context::default());
    enabled.process_event(Run { enabled: true }).unwrap();
    assert_eq!(enabled.context().order, ["before", "conditional", "after"]);

    let mut disabled = EvalDslStateMachine::new(Context::default());
    disabled.process_event(Run { enabled: false }).unwrap();
    assert_eq!(disabled.context().order, ["before", "after"]);
}

pub struct AsyncRun;

sml! {
    AsyncEvalDsl {
        *"idle"_s + event<AsyncRun> / (async_before, eval [async async_enabled] / async async_conditional) = X,
    }
}

#[derive(Default)]
struct AsyncContext {
    actions: usize,
}

impl AsyncEvalDslStateMachineContext for AsyncContext {
    async fn async_enabled(&self, _event: &AsyncRun) -> Result<bool, ()> {
        Ok(true)
    }

    fn async_before(&mut self, _event: &AsyncRun) -> Result<(), ()> {
        self.actions += 1;
        Ok(())
    }

    async fn async_conditional(&mut self, _event: &AsyncRun) -> Result<(), ()> {
        self.actions += 1;
        Ok(())
    }
}

#[test]
fn eval_supports_async_guards_and_actions() {
    smol::block_on(async {
        let mut sm = AsyncEvalDslStateMachine::new(AsyncContext::default());
        sm.process_event(AsyncRun).await.unwrap();
        assert_eq!(sm.context().actions, 2);
    });
}