automato 14.0.0

a crate to generate state machines enforced at compile time
Documentation
use async_trait::async_trait;
use automato::statemachine;
use futures::executor::block_on;
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize)]
pub struct JobData {}

#[derive(Serialize, Deserialize)]
pub struct QueuedData {}

#[derive(Serialize, Deserialize)]
pub struct ProcessingData {}

#[derive(Serialize, Deserialize)]
pub struct CompletedData {}

statemachine! {
    Job: JobData {
        init Queued: QueuedData {
            start => Processing
        },
        Processing: ProcessingData {
            complete => Completed,
            queue => Queued
        },
        Completed: CompletedData {}
    }
}

struct Log {}

#[async_trait]
impl Observer for Log {
    type Error = ();
}

#[test]
fn init() {
    let _job = block_on(Job::init(
        Log {},
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();
}

#[test]
fn init_without_id() {
    let result = block_on(Job::init(Log {}, None, JobData {}, QueuedData {}));
    let err = result.err().unwrap();
    match err {
        InitError::EmptyId => {}
        _ => panic!("expected InitErr::EmptyId"),
    };
}

#[test]
fn init_with_deferred_id() {
    struct DeferredIdInitLog {}

    #[async_trait]
    impl Observer for DeferredIdInitLog {
        type Error = ();

        async fn on_init<T: Serialize + Send, U: Serialize + Send>(
            &mut self,
            _id: Option<String>,
            _to: State,
            _data: Option<T>,
            _state_data: Option<U>,
        ) -> Result<Option<String>, Self::Error> {
            Ok(Some("foo".to_string()))
        }
    }

    let _job = block_on(Job::init(
        DeferredIdInitLog {},
        None,
        JobData {},
        QueuedData {},
    ))
    .unwrap();
}

#[test]
fn on_init() {
    struct InitLog {
        initiated_to_state: Option<State>,
    }

    #[async_trait]
    impl Observer for &mut InitLog {
        type Error = ();

        async fn on_init<T: Serialize + Send, U: Serialize + Send>(
            &mut self,
            id: Option<String>,
            to: State,
            _data: Option<T>,
            _state_data: Option<U>,
        ) -> Result<Option<String>, Self::Error> {
            self.initiated_to_state = Some(to);
            Ok(id)
        }
    }

    let mut init_log = InitLog {
        initiated_to_state: None,
    };

    let _job = block_on(Job::init(
        &mut init_log,
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();

    match init_log.initiated_to_state {
        Some(state) => assert_eq!("Queued", state.to_string()),
        None => panic!("expected some initiated_to_state value"),
    };
}

#[test]
fn read_id() {
    let job = block_on(Job::init(
        Log {},
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();
    let id = job.id();

    assert_eq!(id, "foo");
}

#[test]
fn read_data() {
    let job = block_on(Job::init(
        Log {},
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();
    let _job_data = job.data();
    let _job_state_data = job.state.data();
}

#[test]
fn transition() {
    let job = block_on(Job::init(
        Log {},
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();
    let _job = block_on(job.start(Log {}, ProcessingData {})).unwrap();
}

#[test]
fn on_transition() {
    struct TransitionLog {
        from: Option<State>,
        to: Option<State>,
    }

    #[async_trait]
    impl Observer for &mut TransitionLog {
        type Error = ();

        async fn on_transition<T: Serialize + Send, U: Serialize + Send>(
            &mut self,
            _id: &str,
            from: State,
            to: State,
            _data: Option<T>,
            _state_data: Option<U>,
        ) -> Result<(), Self::Error> {
            self.from = Some(from);
            self.to = Some(to);
            Ok(())
        }
    }

    let mut transition_log = TransitionLog {
        from: None,
        to: None,
    };

    let job = block_on(Job::init(
        &mut transition_log,
        Some("foo".to_string()),
        JobData {},
        QueuedData {},
    ))
    .unwrap();
    let _job = block_on(job.start(&mut transition_log, ProcessingData {})).unwrap();

    match transition_log.from {
        Some(state) => assert_eq!("Queued", state.to_string()),
        None => panic!("expected some from value"),
    };

    match transition_log.to {
        Some(state) => assert_eq!("Processing", state.to_string()),
        None => panic!("expected some to value"),
    };
}