statum-graph 0.7.0

Static graph export for Statum machine introspection
Documentation
#![allow(dead_code)]

use statum_graph::{CodebaseDoc, CodebaseRelationBasis, CodebaseRelationSource};

mod workflow {
    use statum::{machine, state, transition};

    #[state]
    pub enum State {
        Rejected,
        Consumed,
        Destroyed,
    }

    #[machine]
    pub struct Machine<State> {}

    #[transition]
    impl Machine<Rejected> {
        fn destroy(self) -> Machine<Destroyed> {
            self.transition()
        }
    }

    #[transition]
    impl Machine<Consumed> {
        fn destroy(self) -> Machine<Destroyed> {
            self.transition()
        }
    }
}

mod audit {
    use statum::{machine, state, transition};

    #[state]
    pub enum State {
        Pending,
        Closed,
    }

    #[machine]
    pub struct Machine<State> {}

    #[transition]
    impl Machine<Pending> {
        fn close(
            self,
            #[via(crate::workflow::machine::via::Destroy)] destroyed: crate::workflow::Machine<
                crate::workflow::Destroyed,
            >,
        ) -> Machine<Closed> {
            let _ = destroyed;
            self.transition()
        }
    }
}

#[test]
fn duplicate_attested_route_names_group_compatible_producers() {
    let doc = CodebaseDoc::linked().expect("linked codebase doc");

    let workflow = doc
        .machines()
        .iter()
        .find(|machine| machine.rust_type_path.ends_with("workflow::Machine"))
        .expect("workflow machine");
    let audit = doc
        .machines()
        .iter()
        .find(|machine| machine.rust_type_path.ends_with("audit::Machine"))
        .expect("audit machine");
    let rejected = workflow
        .states
        .iter()
        .find(|state| state.rust_name == "Rejected")
        .expect("rejected state");
    let consumed = workflow
        .states
        .iter()
        .find(|state| state.rust_name == "Consumed")
        .expect("consumed state");
    let closed_transition = audit
        .transitions
        .iter()
        .find(|transition| transition.method_name == "close")
        .expect("close transition");

    let relation = doc
        .relations()
        .iter()
        .find(|relation| {
            relation.basis == CodebaseRelationBasis::ViaDeclaration
                && matches!(
                    relation.source,
                    CodebaseRelationSource::TransitionParam {
                        machine,
                        transition,
                        param_name: Some("destroyed"),
                        ..
                    } if machine == audit.index && transition == closed_transition.index
                )
        })
        .expect("attested relation");
    let attested = relation.attested_via.as_ref().expect("attested route");
    assert_eq!(attested.route_name, "Destroy");
    assert_eq!(attested.producers.len(), 2);
    assert_eq!(
        attested
            .producers
            .iter()
            .map(|producer| producer.state)
            .collect::<Vec<_>>(),
        vec![rejected.index, consumed.index]
    );

    let detail = doc
        .relation_detail(relation.index)
        .expect("attested relation detail");
    assert_eq!(detail.attested_via_machine, None);
    assert_eq!(detail.attested_via_state, None);
    assert_eq!(detail.attested_via_transition, None);
    assert_eq!(detail.attested_via_producers.len(), 2);
    assert_eq!(
        detail
            .attested_via_producers
            .iter()
            .map(|producer| producer.state.rust_name)
            .collect::<Vec<_>>(),
        vec!["Rejected", "Consumed"]
    );
    assert_eq!(
        detail
            .attested_via_producers
            .iter()
            .map(|producer| producer.transition.method_name)
            .collect::<Vec<_>>(),
        vec!["destroy", "destroy"]
    );
}