1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", rename_all = "snake_case")]
10pub enum NodeEvent {
11 NodeStarting {
12 node_id: u32,
13 },
14 NodeStarted {
15 node_id: u32,
16 pid: u32,
17 },
18 NodeStopping {
19 node_id: u32,
20 },
21 NodeStopped {
22 node_id: u32,
23 },
24 NodeCrashed {
25 node_id: u32,
26 exit_code: Option<i32>,
27 },
28 NodeRestarting {
29 node_id: u32,
30 attempt: u32,
31 },
32 NodeErrored {
33 node_id: u32,
34 message: String,
35 },
36 DownloadStarted {
37 version: String,
38 },
39 DownloadProgress {
40 bytes: u64,
41 total: u64,
42 },
43 DownloadComplete {
44 version: String,
45 path: PathBuf,
46 },
47 UpgradeScheduled {
50 node_id: u32,
51 pending_version: String,
52 },
53 NodeUpgraded {
56 node_id: u32,
57 old_version: String,
58 new_version: String,
59 },
60}
61
62impl NodeEvent {
63 pub fn event_type(&self) -> &'static str {
65 match self {
66 NodeEvent::NodeStarting { .. } => "node_starting",
67 NodeEvent::NodeStarted { .. } => "node_started",
68 NodeEvent::NodeStopping { .. } => "node_stopping",
69 NodeEvent::NodeStopped { .. } => "node_stopped",
70 NodeEvent::NodeCrashed { .. } => "node_crashed",
71 NodeEvent::NodeRestarting { .. } => "node_restarting",
72 NodeEvent::NodeErrored { .. } => "node_errored",
73 NodeEvent::DownloadStarted { .. } => "download_started",
74 NodeEvent::DownloadProgress { .. } => "download_progress",
75 NodeEvent::DownloadComplete { .. } => "download_complete",
76 NodeEvent::UpgradeScheduled { .. } => "upgrade_scheduled",
77 NodeEvent::NodeUpgraded { .. } => "node_upgraded",
78 }
79 }
80}
81
82pub trait EventListener: Send + Sync {
84 fn on_event(&self, event: NodeEvent);
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn event_serializes_with_type_tag() {
93 let event = NodeEvent::NodeStarted {
94 node_id: 1,
95 pid: 1234,
96 };
97 let json = serde_json::to_string(&event).unwrap();
98 assert!(json.contains("\"type\":\"node_started\""));
99 assert!(json.contains("\"node_id\":1"));
100 assert!(json.contains("\"pid\":1234"));
101 }
102
103 #[test]
104 fn event_type_matches_serde_tag() {
105 let event = NodeEvent::NodeCrashed {
106 node_id: 2,
107 exit_code: Some(1),
108 };
109 assert_eq!(event.event_type(), "node_crashed");
110
111 let json = serde_json::to_string(&event).unwrap();
113 assert!(json.contains(&format!("\"type\":\"{}\"", event.event_type())));
114 }
115
116 #[test]
117 fn event_roundtrips() {
118 let event = NodeEvent::DownloadProgress {
119 bytes: 1024,
120 total: 4096,
121 };
122 let json = serde_json::to_string(&event).unwrap();
123 let deserialized: NodeEvent = serde_json::from_str(&json).unwrap();
124 assert_eq!(deserialized.event_type(), "download_progress");
125 }
126
127 #[test]
128 fn upgrade_scheduled_event_serializes() {
129 let event = NodeEvent::UpgradeScheduled {
130 node_id: 2,
131 pending_version: "0.10.11-rc.1".to_string(),
132 };
133 let json = serde_json::to_string(&event).unwrap();
134 assert!(json.contains("\"type\":\"upgrade_scheduled\""));
135 assert!(json.contains("\"node_id\":2"));
136 assert!(json.contains("\"pending_version\":\"0.10.11-rc.1\""));
137 assert_eq!(event.event_type(), "upgrade_scheduled");
138 }
139
140 #[test]
141 fn node_upgraded_event_serializes() {
142 let event = NodeEvent::NodeUpgraded {
143 node_id: 3,
144 old_version: "0.10.1".to_string(),
145 new_version: "0.10.11-rc.1".to_string(),
146 };
147 let json = serde_json::to_string(&event).unwrap();
148 assert!(json.contains("\"type\":\"node_upgraded\""));
149 assert!(json.contains("\"old_version\":\"0.10.1\""));
150 assert!(json.contains("\"new_version\":\"0.10.11-rc.1\""));
151 assert_eq!(event.event_type(), "node_upgraded");
152 }
153}