Skip to main content

shadi_mas/
runtime.rs

1use crate::types::{EventId, EventOutcome, PatternKind, RuntimeCounters, SemanticEvent};
2
3pub trait CoordinationEngine: Send + Sync {
4    fn pattern(&self) -> PatternKind;
5    fn apply(&mut self, event: SemanticEvent) -> EventOutcome;
6    fn counters(&self) -> RuntimeCounters;
7}
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct AppliedTransition {
11    pub event_id: EventId,
12    pub outcome: EventOutcome,
13}
14
15#[derive(Debug)]
16pub struct MasRuntime<E> {
17    engine: E,
18    history: Vec<AppliedTransition>,
19}
20
21impl<E> MasRuntime<E>
22where
23    E: CoordinationEngine,
24{
25    pub fn new(engine: E) -> Self {
26        Self {
27            engine,
28            history: Vec::new(),
29        }
30    }
31
32    pub fn apply(&mut self, event: SemanticEvent) -> EventOutcome {
33        let event_id = event.metadata.event_id.clone();
34        let outcome = self.engine.apply(event);
35        self.history.push(AppliedTransition {
36            event_id,
37            outcome: outcome.clone(),
38        });
39        outcome
40    }
41
42    pub fn engine(&self) -> &E {
43        &self.engine
44    }
45
46    pub fn engine_mut(&mut self) -> &mut E {
47        &mut self.engine
48    }
49
50    pub fn history(&self) -> &[AppliedTransition] {
51        &self.history
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::engines::development::{DevelopmentEngine, DevelopmentEngineConfig};
59    use crate::types::{
60        AgentId, Epoch, EventId, EventMetadata, EventOutcome, EventSource, SemanticEvent,
61        SemanticPayload,
62    };
63
64    fn make_runtime() -> MasRuntime<DevelopmentEngine> {
65        let config = DevelopmentEngineConfig::new(
66            [AgentId::from("a"), AgentId::from("b")],
67            2,
68            10,
69        );
70        MasRuntime::new(DevelopmentEngine::new(Epoch(0), config))
71    }
72
73    fn proposal(id: &str, participant: &str, value: i64) -> SemanticEvent {
74        SemanticEvent {
75            pattern: PatternKind::Development,
76            metadata: EventMetadata {
77                event_id: EventId::from(id),
78                correlation_id: None,
79                epoch: Epoch(0),
80                source: EventSource::Peer(AgentId::from(participant)),
81            },
82            payload: SemanticPayload::ExternalBytes(value.to_le_bytes().to_vec()),
83        }
84    }
85
86    #[test]
87    fn engine_mut_returns_mutable_reference() {
88        let mut rt = make_runtime();
89        // Verify engine_mut returns a usable mutable reference.
90        let engine = rt.engine_mut();
91        assert_eq!(engine.active_epoch(), Epoch(0));
92    }
93
94    #[test]
95    fn history_accumulates_all_transitions() {
96        let mut rt = make_runtime();
97        rt.apply(proposal("e1", "a", 10));
98        rt.apply(proposal("e2", "b", 20));
99
100        let h = rt.history();
101        assert_eq!(h.len(), 2);
102        assert_eq!(h[0].event_id, EventId::from("e1"));
103        assert_eq!(h[0].outcome, EventOutcome::Applied);
104        assert_eq!(h[1].event_id, EventId::from("e2"));
105        assert!(matches!(h[1].outcome, EventOutcome::Finalized(_)));
106    }
107
108    #[test]
109    fn history_is_empty_on_new_runtime() {
110        let rt = make_runtime();
111        assert!(rt.history().is_empty());
112    }
113}