1use std::collections::HashMap;
9use std::sync::{Arc, Mutex};
10
11use serde::{Deserialize, Serialize};
12
13use super::wps::{WaveEvent, WpsClient, EVENT_SYS_INFO, EVENT_BLOCK_STATS, EVENT_BLOCK_FILE};
14
15pub const WS_EVENT_RPC: &str = "rpc";
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum Lane {
28 Priority,
29 Background,
30}
31
32pub struct WsReceivers {
36 pub priority: tokio::sync::mpsc::UnboundedReceiver<serde_json::Value>,
37 pub background: tokio::sync::mpsc::UnboundedReceiver<serde_json::Value>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct WSEventType {
44 pub eventtype: String,
45 #[serde(skip_serializing_if = "String::is_empty", default)]
46 pub oref: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub data: Option<serde_json::Value>,
49}
50
51struct WindowWatchData {
52 priority: tokio::sync::mpsc::UnboundedSender<serde_json::Value>,
54 background: tokio::sync::mpsc::UnboundedSender<serde_json::Value>,
56 #[allow(dead_code)]
57 tab_id: String,
58}
59
60impl WindowWatchData {
61 fn sender(&self, lane: Lane) -> &tokio::sync::mpsc::UnboundedSender<serde_json::Value> {
62 match lane {
63 Lane::Priority => &self.priority,
64 Lane::Background => &self.background,
65 }
66 }
67}
68
69pub struct EventBus {
71 watches: Mutex<HashMap<String, WindowWatchData>>,
72}
73
74impl EventBus {
75 pub fn new() -> Self {
76 Self {
77 watches: Mutex::new(HashMap::new()),
78 }
79 }
80
81 pub fn register_ws(&self, conn_id: &str, tab_id: &str) -> WsReceivers {
84 let (priority_tx, priority_rx) = tokio::sync::mpsc::unbounded_channel();
85 let (background_tx, background_rx) = tokio::sync::mpsc::unbounded_channel();
86 let mut watches = self.watches.lock().unwrap();
87 watches.insert(
88 conn_id.to_string(),
89 WindowWatchData {
90 priority: priority_tx,
91 background: background_tx,
92 tab_id: tab_id.to_string(),
93 },
94 );
95 WsReceivers {
96 priority: priority_rx,
97 background: background_rx,
98 }
99 }
100
101 pub fn unregister_ws(&self, conn_id: &str) {
103 let mut watches = self.watches.lock().unwrap();
104 watches.remove(conn_id);
105 }
106
107 #[allow(dead_code)]
109 pub fn has_connections_for(&self, tab_id: &str) -> bool {
110 let watches = self.watches.lock().unwrap();
111 watches.values().any(|w| w.tab_id == tab_id)
112 }
113
114 #[allow(dead_code)]
116 pub async fn wait_for_connection(
117 &self,
118 tab_id: &str,
119 timeout: std::time::Duration,
120 ) -> bool {
121 let deadline = tokio::time::Instant::now() + timeout;
122 loop {
123 if self.has_connections_for(tab_id) {
124 return true;
125 }
126 if tokio::time::Instant::now() >= deadline {
127 return false;
128 }
129 tokio::time::sleep(std::time::Duration::from_millis(20)).await;
130 }
131 }
132
133 pub fn send_to_conn(&self, conn_id: &str, event: &WSEventType) {
136 self.send_to_conn_lane(conn_id, event, Lane::Priority);
137 }
138
139 pub fn send_to_conn_lane(&self, conn_id: &str, event: &WSEventType, lane: Lane) {
142 let data = match serde_json::to_value(event) {
143 Ok(v) => v,
144 Err(e) => {
145 tracing::error!("cannot marshal event: {}", e);
146 return;
147 }
148 };
149 let watches = self.watches.lock().unwrap();
150 if let Some(watch) = watches.get(conn_id) {
151 if watch.sender(lane).send(data).is_err() {
152 tracing::warn!("failed to send event to conn {}", conn_id);
153 }
154 }
155 }
156
157 pub fn broadcast_event(&self, event: &WSEventType) {
159 self.broadcast_event_lane(event, Lane::Priority);
160 }
161
162 pub fn broadcast_event_lane(&self, event: &WSEventType, lane: Lane) {
164 let data = match serde_json::to_value(event) {
165 Ok(v) => v,
166 Err(e) => {
167 tracing::error!("cannot marshal event: {}", e);
168 return;
169 }
170 };
171 let watches = self.watches.lock().unwrap();
172 for (conn_id, watch) in watches.iter() {
173 if watch.sender(lane).send(data.clone()).is_err() {
174 tracing::warn!("failed to send event to conn {}", conn_id);
175 }
176 }
177 }
178
179 #[allow(dead_code)]
181 pub fn send_to_tab(&self, tab_id: &str, event: &WSEventType) {
182 let data = match serde_json::to_value(event) {
183 Ok(v) => v,
184 Err(e) => {
185 tracing::error!("cannot marshal event: {}", e);
186 return;
187 }
188 };
189 let watches = self.watches.lock().unwrap();
190 for watch in watches.values() {
191 if watch.tab_id == tab_id {
192 let _ = watch.sender(Lane::Priority).send(data.clone());
193 }
194 }
195 }
196
197 pub fn connection_count(&self) -> usize {
199 self.watches.lock().unwrap().len()
200 }
201}
202
203impl Default for EventBus {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209pub struct EventBusBridge {
212 event_bus: Arc<EventBus>,
213}
214
215impl EventBusBridge {
216 pub fn new(event_bus: Arc<EventBus>) -> Self {
217 Self { event_bus }
218 }
219}
220
221impl WpsClient for EventBusBridge {
222 fn send_event(&self, route_id: &str, event: WaveEvent) {
223 let lane = match event.event.as_str() {
229 EVENT_SYS_INFO | EVENT_BLOCK_STATS => Lane::Background,
230 _ => Lane::Priority,
231 };
232 let ws_event = WSEventType {
234 eventtype: WS_EVENT_RPC.to_string(),
235 oref: String::new(),
236 data: Some(serde_json::json!({
237 "command": "eventrecv",
238 "data": event
239 })),
240 };
241 if route_id == "ws-main" {
245 self.event_bus.broadcast_event_lane(&ws_event, lane);
246 } else {
247 self.event_bus.send_to_conn_lane(route_id, &ws_event, lane);
248 }
249 }
250}
251
252#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn test_register_unregister() {
262 let bus = EventBus::new();
263 let _rx = bus.register_ws("conn-1", "tab-1");
264 assert_eq!(bus.connection_count(), 1);
265 assert!(bus.has_connections_for("tab-1"));
266 assert!(!bus.has_connections_for("tab-2"));
267
268 bus.unregister_ws("conn-1");
269 assert_eq!(bus.connection_count(), 0);
270 assert!(!bus.has_connections_for("tab-1"));
271 }
272
273 #[test]
274 fn test_broadcast_event() {
275 let bus = EventBus::new();
276 let mut rx1 = bus.register_ws("conn-1", "tab-1");
277 let mut rx2 = bus.register_ws("conn-2", "tab-2");
278
279 let event = WSEventType {
280 eventtype: WS_EVENT_RPC.to_string(),
281 oref: String::new(),
282 data: Some(serde_json::json!({"test": true})),
283 };
284 bus.broadcast_event(&event);
285
286 assert!(rx1.priority.try_recv().is_ok());
288 assert!(rx2.priority.try_recv().is_ok());
289 assert!(rx1.background.try_recv().is_err());
290 }
291
292 #[test]
293 fn test_send_to_tab() {
294 let bus = EventBus::new();
295 let mut rx1 = bus.register_ws("conn-1", "tab-1");
296 let mut rx2 = bus.register_ws("conn-2", "tab-2");
297
298 let event = WSEventType {
299 eventtype: WS_EVENT_RPC.to_string(),
300 oref: String::new(),
301 data: None,
302 };
303 bus.send_to_tab("tab-1", &event);
304
305 assert!(rx1.priority.try_recv().is_ok());
306 assert!(rx2.priority.try_recv().is_err()); }
308
309 #[test]
310 fn test_lane_separation() {
311 let bus = EventBus::new();
315 let mut rx = bus.register_ws("conn-1", "tab-1");
316
317 let event = WSEventType {
318 eventtype: WS_EVENT_RPC.to_string(),
319 oref: String::new(),
320 data: None,
321 };
322 bus.send_to_conn_lane("conn-1", &event, Lane::Background);
323 assert!(rx.priority.try_recv().is_err()); assert!(rx.background.try_recv().is_ok()); bus.send_to_conn_lane("conn-1", &event, Lane::Priority);
327 assert!(rx.background.try_recv().is_err()); assert!(rx.priority.try_recv().is_ok()); }
330
331 #[test]
332 fn test_bridge_routes_telemetry_to_background() {
333 let bus = Arc::new(EventBus::new());
336 let mut rx = bus.register_ws("conn-1", "tab-1");
337 let bridge = EventBusBridge::new(bus.clone());
338
339 let telemetry = |event: &str| WaveEvent {
340 event: event.to_string(),
341 scopes: vec![],
342 sender: String::new(),
343 persist: 0,
344 data: None,
345 };
346
347 bridge.send_event("conn-1", telemetry(EVENT_SYS_INFO));
348 bridge.send_event("conn-1", telemetry(EVENT_BLOCK_STATS));
349 assert!(rx.priority.try_recv().is_err()); assert!(rx.background.try_recv().is_ok()); assert!(rx.background.try_recv().is_ok()); bridge.send_event("conn-1", telemetry(EVENT_BLOCK_FILE));
355 assert!(rx.priority.try_recv().is_ok());
356 assert!(rx.background.try_recv().is_err());
357 }
358
359 #[test]
360 fn test_ws_event_serialization() {
361 let event = WSEventType {
362 eventtype: "test".to_string(),
363 oref: String::new(),
364 data: Some(serde_json::json!(42)),
365 };
366 let json = serde_json::to_string(&event).unwrap();
367 assert!(json.contains("\"eventtype\":\"test\""));
368 assert!(!json.contains("\"oref\""));
370 }
371}