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 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}