Skip to main content

libpetri_event/
event_store.rs

1use crate::net_event::NetEvent;
2
3/// Trait for event storage during Petri net execution.
4///
5/// The `ENABLED` constant allows zero-cost elimination of event recording
6/// when using `NoopEventStore` — the compiler eliminates all branches
7/// guarded by `if E::ENABLED`.
8pub trait EventStore: Default + Send {
9    const ENABLED: bool;
10
11    fn append(&mut self, event: NetEvent);
12    fn events(&self) -> &[NetEvent];
13    fn size(&self) -> usize;
14    fn is_empty(&self) -> bool;
15}
16
17/// No-op event store for production use.
18///
19/// All methods are no-ops; the compiler eliminates recording branches
20/// via the `ENABLED = false` constant.
21#[derive(Debug, Default)]
22pub struct NoopEventStore;
23
24impl EventStore for NoopEventStore {
25    const ENABLED: bool = false;
26
27    #[inline(always)]
28    fn append(&mut self, _event: NetEvent) {}
29
30    #[inline(always)]
31    fn events(&self) -> &[NetEvent] {
32        &[]
33    }
34
35    #[inline(always)]
36    fn size(&self) -> usize {
37        0
38    }
39
40    #[inline(always)]
41    fn is_empty(&self) -> bool {
42        true
43    }
44}
45
46/// In-memory event store for debugging and testing.
47#[derive(Debug, Default)]
48pub struct InMemoryEventStore {
49    events: Vec<NetEvent>,
50}
51
52impl InMemoryEventStore {
53    pub fn new() -> Self {
54        Self { events: Vec::new() }
55    }
56
57    /// Filter events by predicate.
58    pub fn filter(&self, predicate: impl Fn(&NetEvent) -> bool) -> Vec<&NetEvent> {
59        self.events.iter().filter(|e| predicate(e)).collect()
60    }
61
62    /// Get events for a specific transition.
63    pub fn transition_events(&self, name: &str) -> Vec<&NetEvent> {
64        self.filter(|e| e.transition_name() == Some(name))
65    }
66
67    /// Get all failure events.
68    pub fn failures(&self) -> Vec<&NetEvent> {
69        self.filter(|e| e.is_failure())
70    }
71}
72
73impl EventStore for InMemoryEventStore {
74    const ENABLED: bool = true;
75
76    fn append(&mut self, event: NetEvent) {
77        self.events.push(event);
78    }
79
80    fn events(&self) -> &[NetEvent] {
81        &self.events
82    }
83
84    fn size(&self) -> usize {
85        self.events.len()
86    }
87
88    fn is_empty(&self) -> bool {
89        self.events.is_empty()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use std::sync::Arc;
97
98    #[test]
99    fn noop_store_is_always_empty() {
100        let mut store = NoopEventStore;
101        store.append(NetEvent::ExecutionStarted {
102            net_name: Arc::from("test"),
103            timestamp: 0,
104        });
105        assert!(store.is_empty());
106        assert_eq!(store.size(), 0);
107        assert!(store.events().is_empty());
108    }
109
110    #[test]
111    fn in_memory_store_records_events() {
112        let mut store = InMemoryEventStore::new();
113        store.append(NetEvent::ExecutionStarted {
114            net_name: Arc::from("test"),
115            timestamp: 0,
116        });
117        store.append(NetEvent::TransitionStarted {
118            transition_name: Arc::from("t1"),
119            timestamp: 1,
120        });
121        assert_eq!(store.size(), 2);
122        assert!(!store.is_empty());
123    }
124
125    #[test]
126    fn in_memory_store_transition_events() {
127        let mut store = InMemoryEventStore::new();
128        store.append(NetEvent::TransitionStarted {
129            transition_name: Arc::from("t1"),
130            timestamp: 1,
131        });
132        store.append(NetEvent::TransitionCompleted {
133            transition_name: Arc::from("t1"),
134            timestamp: 2,
135        });
136        store.append(NetEvent::TransitionStarted {
137            transition_name: Arc::from("t2"),
138            timestamp: 3,
139        });
140        assert_eq!(store.transition_events("t1").len(), 2);
141        assert_eq!(store.transition_events("t2").len(), 1);
142    }
143
144    #[test]
145    fn enabled_constants() {
146        const { assert!(!NoopEventStore::ENABLED) };
147        const { assert!(InMemoryEventStore::ENABLED) };
148    }
149}