Skip to main content

libpetri_debug/
net_event_converter.rs

1//! Converts Rust `NetEvent` instances to serializable `NetEventInfo`.
2
3use std::collections::HashMap;
4
5use libpetri_event::net_event::NetEvent;
6
7use crate::debug_response::NetEventInfo;
8
9/// Converts a `NetEvent` to a serializable `NetEventInfo`.
10pub fn to_event_info(event: &NetEvent) -> NetEventInfo {
11    match event {
12        NetEvent::ExecutionStarted {
13            net_name,
14            timestamp,
15        } => NetEventInfo {
16            event_type: "ExecutionStarted".into(),
17            timestamp: format_timestamp(*timestamp),
18            transition_name: None,
19            place_name: None,
20            details: HashMap::from([(
21                "netName".into(),
22                serde_json::Value::String(net_name.to_string()),
23            )]),
24        },
25        NetEvent::ExecutionCompleted {
26            net_name,
27            timestamp,
28        } => NetEventInfo {
29            event_type: "ExecutionCompleted".into(),
30            timestamp: format_timestamp(*timestamp),
31            transition_name: None,
32            place_name: None,
33            details: HashMap::from([(
34                "netName".into(),
35                serde_json::Value::String(net_name.to_string()),
36            )]),
37        },
38        NetEvent::TransitionEnabled {
39            transition_name,
40            timestamp,
41        } => NetEventInfo {
42            event_type: "TransitionEnabled".into(),
43            timestamp: format_timestamp(*timestamp),
44            transition_name: Some(transition_name.to_string()),
45            place_name: None,
46            details: HashMap::new(),
47        },
48        NetEvent::TransitionClockRestarted {
49            transition_name,
50            timestamp,
51        } => NetEventInfo {
52            event_type: "TransitionClockRestarted".into(),
53            timestamp: format_timestamp(*timestamp),
54            transition_name: Some(transition_name.to_string()),
55            place_name: None,
56            details: HashMap::new(),
57        },
58        NetEvent::TransitionStarted {
59            transition_name,
60            timestamp,
61        } => NetEventInfo {
62            event_type: "TransitionStarted".into(),
63            timestamp: format_timestamp(*timestamp),
64            transition_name: Some(transition_name.to_string()),
65            place_name: None,
66            details: HashMap::new(),
67        },
68        NetEvent::TransitionCompleted {
69            transition_name,
70            timestamp,
71        } => NetEventInfo {
72            event_type: "TransitionCompleted".into(),
73            timestamp: format_timestamp(*timestamp),
74            transition_name: Some(transition_name.to_string()),
75            place_name: None,
76            details: HashMap::new(),
77        },
78        NetEvent::TransitionFailed {
79            transition_name,
80            error,
81            timestamp,
82        } => NetEventInfo {
83            event_type: "TransitionFailed".into(),
84            timestamp: format_timestamp(*timestamp),
85            transition_name: Some(transition_name.to_string()),
86            place_name: None,
87            details: HashMap::from([(
88                "errorMessage".into(),
89                serde_json::Value::String(error.clone()),
90            )]),
91        },
92        NetEvent::TransitionTimedOut {
93            transition_name,
94            timestamp,
95        } => NetEventInfo {
96            event_type: "TransitionTimedOut".into(),
97            timestamp: format_timestamp(*timestamp),
98            transition_name: Some(transition_name.to_string()),
99            place_name: None,
100            details: HashMap::new(),
101        },
102        NetEvent::ActionTimedOut {
103            transition_name,
104            timeout_ms,
105            timestamp,
106        } => NetEventInfo {
107            event_type: "ActionTimedOut".into(),
108            timestamp: format_timestamp(*timestamp),
109            transition_name: Some(transition_name.to_string()),
110            place_name: None,
111            details: HashMap::from([("timeoutMs".into(), serde_json::json!(*timeout_ms))]),
112        },
113        NetEvent::TokenAdded {
114            place_name,
115            timestamp,
116        } => NetEventInfo {
117            event_type: "TokenAdded".into(),
118            timestamp: format_timestamp(*timestamp),
119            transition_name: None,
120            place_name: Some(place_name.to_string()),
121            details: HashMap::new(),
122        },
123        NetEvent::TokenRemoved {
124            place_name,
125            timestamp,
126        } => NetEventInfo {
127            event_type: "TokenRemoved".into(),
128            timestamp: format_timestamp(*timestamp),
129            transition_name: None,
130            place_name: Some(place_name.to_string()),
131            details: HashMap::new(),
132        },
133        NetEvent::LogMessage {
134            transition_name,
135            level,
136            message,
137            timestamp,
138        } => NetEventInfo {
139            event_type: "LogMessage".into(),
140            timestamp: format_timestamp(*timestamp),
141            transition_name: Some(transition_name.to_string()),
142            place_name: None,
143            details: HashMap::from([
144                ("level".into(), serde_json::Value::String(level.clone())),
145                ("message".into(), serde_json::Value::String(message.clone())),
146            ]),
147        },
148        NetEvent::MarkingSnapshot { marking, timestamp } => {
149            let marking_map: HashMap<String, usize> =
150                marking.iter().map(|(k, v)| (k.to_string(), *v)).collect();
151            NetEventInfo {
152                event_type: "MarkingSnapshot".into(),
153                timestamp: format_timestamp(*timestamp),
154                transition_name: None,
155                place_name: None,
156                details: HashMap::from([("marking".into(), serde_json::json!(marking_map))]),
157            }
158        }
159    }
160}
161
162/// Formats a timestamp (milliseconds since epoch) as a string.
163/// Uses simple numeric format since Rust's std doesn't have ISO-8601 formatting.
164fn format_timestamp(ms: u64) -> String {
165    ms.to_string()
166}
167
168/// Extracts a transition name from an event, if applicable.
169pub fn extract_transition_name(event: &NetEvent) -> Option<&str> {
170    event.transition_name()
171}
172
173/// Extracts a place name from an event, if applicable.
174pub fn extract_place_name(event: &NetEvent) -> Option<&str> {
175    match event {
176        NetEvent::TokenAdded { place_name, .. } | NetEvent::TokenRemoved { place_name, .. } => {
177            Some(place_name)
178        }
179        _ => None,
180    }
181}
182
183/// Maps a Rust `NetEvent` variant to a PascalCase type name string.
184pub fn event_type_name(event: &NetEvent) -> &'static str {
185    match event {
186        NetEvent::ExecutionStarted { .. } => "ExecutionStarted",
187        NetEvent::ExecutionCompleted { .. } => "ExecutionCompleted",
188        NetEvent::TransitionEnabled { .. } => "TransitionEnabled",
189        NetEvent::TransitionClockRestarted { .. } => "TransitionClockRestarted",
190        NetEvent::TransitionStarted { .. } => "TransitionStarted",
191        NetEvent::TransitionCompleted { .. } => "TransitionCompleted",
192        NetEvent::TransitionFailed { .. } => "TransitionFailed",
193        NetEvent::TransitionTimedOut { .. } => "TransitionTimedOut",
194        NetEvent::ActionTimedOut { .. } => "ActionTimedOut",
195        NetEvent::TokenAdded { .. } => "TokenAdded",
196        NetEvent::TokenRemoved { .. } => "TokenRemoved",
197        NetEvent::LogMessage { .. } => "LogMessage",
198        NetEvent::MarkingSnapshot { .. } => "MarkingSnapshot",
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use std::sync::Arc;
206
207    #[test]
208    fn convert_execution_started() {
209        let event = NetEvent::ExecutionStarted {
210            net_name: Arc::from("test"),
211            timestamp: 1000,
212        };
213        let info = to_event_info(&event);
214        assert_eq!(info.event_type, "ExecutionStarted");
215        assert_eq!(info.timestamp, "1000");
216        assert!(info.transition_name.is_none());
217        assert_eq!(info.details["netName"], "test");
218    }
219
220    #[test]
221    fn convert_transition_started() {
222        let event = NetEvent::TransitionStarted {
223            transition_name: Arc::from("t1"),
224            timestamp: 2000,
225        };
226        let info = to_event_info(&event);
227        assert_eq!(info.event_type, "TransitionStarted");
228        assert_eq!(info.transition_name.as_deref(), Some("t1"));
229    }
230
231    #[test]
232    fn convert_token_added() {
233        let event = NetEvent::TokenAdded {
234            place_name: Arc::from("p1"),
235            timestamp: 3000,
236        };
237        let info = to_event_info(&event);
238        assert_eq!(info.event_type, "TokenAdded");
239        assert_eq!(info.place_name.as_deref(), Some("p1"));
240    }
241
242    #[test]
243    fn convert_all_variants() {
244        let events = vec![
245            NetEvent::ExecutionStarted {
246                net_name: Arc::from("n"),
247                timestamp: 0,
248            },
249            NetEvent::ExecutionCompleted {
250                net_name: Arc::from("n"),
251                timestamp: 1,
252            },
253            NetEvent::TransitionEnabled {
254                transition_name: Arc::from("t"),
255                timestamp: 2,
256            },
257            NetEvent::TransitionClockRestarted {
258                transition_name: Arc::from("t"),
259                timestamp: 3,
260            },
261            NetEvent::TransitionStarted {
262                transition_name: Arc::from("t"),
263                timestamp: 4,
264            },
265            NetEvent::TransitionCompleted {
266                transition_name: Arc::from("t"),
267                timestamp: 5,
268            },
269            NetEvent::TransitionFailed {
270                transition_name: Arc::from("t"),
271                error: "err".into(),
272                timestamp: 6,
273            },
274            NetEvent::TransitionTimedOut {
275                transition_name: Arc::from("t"),
276                timestamp: 7,
277            },
278            NetEvent::ActionTimedOut {
279                transition_name: Arc::from("t"),
280                timeout_ms: 100,
281                timestamp: 8,
282            },
283            NetEvent::TokenAdded {
284                place_name: Arc::from("p"),
285                timestamp: 9,
286            },
287            NetEvent::TokenRemoved {
288                place_name: Arc::from("p"),
289                timestamp: 10,
290            },
291            NetEvent::LogMessage {
292                transition_name: Arc::from("t"),
293                level: "INFO".into(),
294                message: "msg".into(),
295                timestamp: 11,
296            },
297            NetEvent::MarkingSnapshot {
298                marking: HashMap::from([(Arc::from("p"), 1)]),
299                timestamp: 12,
300            },
301        ];
302        for event in &events {
303            let info = to_event_info(event);
304            // Verify serializable
305            let _json = serde_json::to_string(&info).unwrap();
306        }
307    }
308
309    #[test]
310    fn event_type_names() {
311        let event = NetEvent::TransitionStarted {
312            transition_name: Arc::from("t"),
313            timestamp: 0,
314        };
315        assert_eq!(event_type_name(&event), "TransitionStarted");
316    }
317}