1use std::collections::{HashMap, VecDeque};
8
9#[derive(Clone, Debug)]
15pub enum NetworkEvent {
16 PeerConnected { peer_id: String, address: String },
18 PeerDisconnected { peer_id: String, reason: String },
20 BlockReceived {
22 from: String,
23 cid: String,
24 size_bytes: usize,
25 },
26 BlockRequested { from: String, cid: String },
28 DhtProviderFound { cid: String, provider: String },
30 GossipMessage {
32 topic: String,
33 from: String,
34 payload_len: usize,
35 },
36 DialFailed {
38 peer_id: String,
39 address: String,
40 error: String,
41 },
42}
43
44#[derive(Clone, Debug)]
50pub enum EventFilter {
51 All,
53 PeerEvents,
55 BlockEvents,
57 DhtEvents,
59 GossipEvents,
61}
62
63const MAX_QUEUE_CAPACITY: usize = 200;
70
71#[derive(Debug)]
73pub struct Subscription {
74 pub id: u64,
76 pub filter: EventFilter,
78 pub queue: VecDeque<NetworkEvent>,
80 pub dropped: u64,
82}
83
84impl Subscription {
85 fn new(id: u64, filter: EventFilter) -> Self {
86 Self {
87 id,
88 filter,
89 queue: VecDeque::new(),
90 dropped: 0,
91 }
92 }
93
94 pub fn matches(&self, event: &NetworkEvent) -> bool {
96 match &self.filter {
97 EventFilter::All => true,
98 EventFilter::PeerEvents => matches!(
99 event,
100 NetworkEvent::PeerConnected { .. }
101 | NetworkEvent::PeerDisconnected { .. }
102 | NetworkEvent::DialFailed { .. }
103 ),
104 EventFilter::BlockEvents => matches!(
105 event,
106 NetworkEvent::BlockReceived { .. } | NetworkEvent::BlockRequested { .. }
107 ),
108 EventFilter::DhtEvents => matches!(event, NetworkEvent::DhtProviderFound { .. }),
109 EventFilter::GossipEvents => matches!(event, NetworkEvent::GossipMessage { .. }),
110 }
111 }
112}
113
114#[derive(Clone, Debug, Default)]
120pub struct BusStats {
121 pub total_published: u64,
123 pub total_delivered: u64,
125 pub total_dropped: u64,
127 pub subscriber_count: usize,
129}
130
131#[derive(Debug, Default)]
154pub struct NetworkEventBus {
155 subscriptions: HashMap<u64, Subscription>,
156 next_id: u64,
157 stats: BusStats,
158}
159
160impl NetworkEventBus {
161 pub fn new() -> Self {
163 Self::default()
164 }
165
166 pub fn subscribe(&mut self, filter: EventFilter) -> u64 {
171 let id = self.next_id;
172 self.next_id += 1;
173 self.subscriptions.insert(id, Subscription::new(id, filter));
174 id
175 }
176
177 pub fn unsubscribe(&mut self, id: u64) -> bool {
181 self.subscriptions.remove(&id).is_some()
182 }
183
184 pub fn publish(&mut self, event: NetworkEvent) {
190 self.stats.total_published += 1;
191
192 for sub in self.subscriptions.values_mut() {
193 if sub.matches(&event) {
194 if sub.queue.len() < MAX_QUEUE_CAPACITY {
195 sub.queue.push_back(event.clone());
196 self.stats.total_delivered += 1;
197 } else {
198 sub.dropped += 1;
199 self.stats.total_dropped += 1;
200 }
201 }
202 }
203 }
204
205 pub fn drain(&mut self, id: u64) -> Vec<NetworkEvent> {
209 match self.subscriptions.get_mut(&id) {
210 Some(sub) => sub.queue.drain(..).collect(),
211 None => Vec::new(),
212 }
213 }
214
215 pub fn peek_count(&self, id: u64) -> usize {
219 self.subscriptions
220 .get(&id)
221 .map(|s| s.queue.len())
222 .unwrap_or(0)
223 }
224
225 pub fn stats(&self) -> BusStats {
227 BusStats {
228 subscriber_count: self.subscriptions.len(),
229 ..self.stats.clone()
230 }
231 }
232
233 pub fn clear_all_queues(&mut self) {
235 for sub in self.subscriptions.values_mut() {
236 sub.queue.clear();
237 }
238 }
239}
240
241#[cfg(test)]
246mod tests {
247 use super::*;
248
249 fn peer_connected() -> NetworkEvent {
250 NetworkEvent::PeerConnected {
251 peer_id: "peer1".into(),
252 address: "/ip4/127.0.0.1/tcp/4001".into(),
253 }
254 }
255
256 fn peer_disconnected() -> NetworkEvent {
257 NetworkEvent::PeerDisconnected {
258 peer_id: "peer1".into(),
259 reason: "timeout".into(),
260 }
261 }
262
263 fn block_received() -> NetworkEvent {
264 NetworkEvent::BlockReceived {
265 from: "peer1".into(),
266 cid: "bafy123".into(),
267 size_bytes: 1024,
268 }
269 }
270
271 fn block_requested() -> NetworkEvent {
272 NetworkEvent::BlockRequested {
273 from: "peer2".into(),
274 cid: "bafy456".into(),
275 }
276 }
277
278 fn dht_provider_found() -> NetworkEvent {
279 NetworkEvent::DhtProviderFound {
280 cid: "bafy789".into(),
281 provider: "peer3".into(),
282 }
283 }
284
285 fn gossip_message() -> NetworkEvent {
286 NetworkEvent::GossipMessage {
287 topic: "blocks".into(),
288 from: "peer4".into(),
289 payload_len: 256,
290 }
291 }
292
293 fn dial_failed() -> NetworkEvent {
294 NetworkEvent::DialFailed {
295 peer_id: "peer5".into(),
296 address: "/ip4/1.2.3.4/tcp/4001".into(),
297 error: "connection refused".into(),
298 }
299 }
300
301 #[test]
303 fn test_new_empty_state() {
304 let bus = NetworkEventBus::new();
305 let stats = bus.stats();
306 assert_eq!(stats.total_published, 0);
307 assert_eq!(stats.total_delivered, 0);
308 assert_eq!(stats.total_dropped, 0);
309 assert_eq!(stats.subscriber_count, 0);
310 }
311
312 #[test]
314 fn test_subscribe_unique_ids() {
315 let mut bus = NetworkEventBus::new();
316 let id1 = bus.subscribe(EventFilter::All);
317 let id2 = bus.subscribe(EventFilter::All);
318 let id3 = bus.subscribe(EventFilter::PeerEvents);
319 assert_ne!(id1, id2);
320 assert_ne!(id2, id3);
321 assert_ne!(id1, id3);
322 }
323
324 #[test]
326 fn test_unsubscribe_true_false() {
327 let mut bus = NetworkEventBus::new();
328 let id = bus.subscribe(EventFilter::All);
329 assert!(bus.unsubscribe(id));
330 assert!(!bus.unsubscribe(id)); assert!(!bus.unsubscribe(9999)); }
333
334 #[test]
336 fn test_publish_delivers_to_all_filter() {
337 let mut bus = NetworkEventBus::new();
338 let id = bus.subscribe(EventFilter::All);
339 bus.publish(peer_connected());
340 bus.publish(block_received());
341 bus.publish(dht_provider_found());
342 assert_eq!(bus.peek_count(id), 3);
343 }
344
345 #[test]
347 fn test_peer_events_filter_matches_peer_connected() {
348 let mut bus = NetworkEventBus::new();
349 let id = bus.subscribe(EventFilter::PeerEvents);
350 bus.publish(peer_connected());
351 assert_eq!(bus.peek_count(id), 1);
352 }
353
354 #[test]
356 fn test_peer_events_filter_ignores_block_received() {
357 let mut bus = NetworkEventBus::new();
358 let id = bus.subscribe(EventFilter::PeerEvents);
359 bus.publish(block_received());
360 assert_eq!(bus.peek_count(id), 0);
361 }
362
363 #[test]
365 fn test_block_events_filter_matches_block_events() {
366 let mut bus = NetworkEventBus::new();
367 let id = bus.subscribe(EventFilter::BlockEvents);
368 bus.publish(block_received());
369 bus.publish(block_requested());
370 bus.publish(peer_connected()); assert_eq!(bus.peek_count(id), 2);
372 }
373
374 #[test]
376 fn test_dht_events_filter_matches_dht_provider_found_only() {
377 let mut bus = NetworkEventBus::new();
378 let id = bus.subscribe(EventFilter::DhtEvents);
379 bus.publish(dht_provider_found());
380 bus.publish(block_received());
381 bus.publish(peer_connected());
382 assert_eq!(bus.peek_count(id), 1);
383 }
384
385 #[test]
387 fn test_gossip_events_filter_matches_gossip_message_only() {
388 let mut bus = NetworkEventBus::new();
389 let id = bus.subscribe(EventFilter::GossipEvents);
390 bus.publish(gossip_message());
391 bus.publish(block_received());
392 bus.publish(dht_provider_found());
393 assert_eq!(bus.peek_count(id), 1);
394 }
395
396 #[test]
398 fn test_drain_returns_buffered_events() {
399 let mut bus = NetworkEventBus::new();
400 let id = bus.subscribe(EventFilter::All);
401 bus.publish(peer_connected());
402 bus.publish(block_received());
403 let events = bus.drain(id);
404 assert_eq!(events.len(), 2);
405 assert_eq!(bus.peek_count(id), 0);
407 }
408
409 #[test]
411 fn test_drain_unknown_id_returns_empty() {
412 let mut bus = NetworkEventBus::new();
413 let events = bus.drain(9999);
414 assert!(events.is_empty());
415 }
416
417 #[test]
419 fn test_peek_count_accurate() {
420 let mut bus = NetworkEventBus::new();
421 let id = bus.subscribe(EventFilter::All);
422 assert_eq!(bus.peek_count(id), 0);
423 bus.publish(peer_connected());
424 assert_eq!(bus.peek_count(id), 1);
425 bus.publish(block_received());
426 assert_eq!(bus.peek_count(id), 2);
427 bus.drain(id);
428 assert_eq!(bus.peek_count(id), 0);
429 }
430
431 #[test]
433 fn test_queue_bounded_at_200_overflow_drops() {
434 let mut bus = NetworkEventBus::new();
435 let id = bus.subscribe(EventFilter::All);
436
437 for _ in 0..205 {
438 bus.publish(peer_connected());
439 }
440
441 assert_eq!(bus.peek_count(id), 200);
442
443 let sub = bus.subscriptions.get(&id).expect("subscription must exist");
444 assert_eq!(sub.dropped, 5);
445 assert_eq!(bus.stats().total_dropped, 5);
446 }
447
448 #[test]
450 fn test_stats_totals_updated_correctly() {
451 let mut bus = NetworkEventBus::new();
452 let id1 = bus.subscribe(EventFilter::All);
453 let id2 = bus.subscribe(EventFilter::PeerEvents);
454
455 bus.publish(peer_connected()); bus.publish(block_received()); let stats = bus.stats();
459 assert_eq!(stats.total_published, 2);
460 assert_eq!(stats.total_delivered, 3);
461 assert_eq!(stats.total_dropped, 0);
462 assert_eq!(stats.subscriber_count, 2);
463
464 let _ = (id1, id2); }
466
467 #[test]
469 fn test_clear_all_queues_empties_all() {
470 let mut bus = NetworkEventBus::new();
471 let id1 = bus.subscribe(EventFilter::All);
472 let id2 = bus.subscribe(EventFilter::BlockEvents);
473 bus.publish(peer_connected());
474 bus.publish(block_received());
475 assert!(bus.peek_count(id1) > 0);
476 assert!(bus.peek_count(id2) > 0);
477
478 bus.clear_all_queues();
479
480 assert_eq!(bus.peek_count(id1), 0);
481 assert_eq!(bus.peek_count(id2), 0);
482 }
483
484 #[test]
486 fn test_multiple_subscribers_each_get_copy() {
487 let mut bus = NetworkEventBus::new();
488 let ids: Vec<u64> = (0..5).map(|_| bus.subscribe(EventFilter::All)).collect();
489
490 bus.publish(peer_connected());
491
492 for id in &ids {
493 assert_eq!(
494 bus.peek_count(*id),
495 1,
496 "subscriber {id} should have 1 event"
497 );
498 }
499 }
500
501 #[test]
503 fn test_unsubscribed_id_no_longer_receives_events() {
504 let mut bus = NetworkEventBus::new();
505 let id = bus.subscribe(EventFilter::All);
506 bus.publish(peer_connected());
507 assert_eq!(bus.peek_count(id), 1);
508
509 assert!(bus.unsubscribe(id));
510 bus.publish(block_received()); assert_eq!(bus.peek_count(id), 0);
514 }
515
516 #[test]
518 fn test_peer_events_filter_matches_all_peer_variants() {
519 let mut bus = NetworkEventBus::new();
520 let id = bus.subscribe(EventFilter::PeerEvents);
521 bus.publish(peer_connected());
522 bus.publish(peer_disconnected());
523 bus.publish(dial_failed());
524 bus.publish(gossip_message()); assert_eq!(bus.peek_count(id), 3);
526 }
527
528 #[test]
530 fn test_subscriber_count_reflects_current() {
531 let mut bus = NetworkEventBus::new();
532 assert_eq!(bus.stats().subscriber_count, 0);
533 let id1 = bus.subscribe(EventFilter::All);
534 assert_eq!(bus.stats().subscriber_count, 1);
535 let id2 = bus.subscribe(EventFilter::BlockEvents);
536 assert_eq!(bus.stats().subscriber_count, 2);
537 bus.unsubscribe(id1);
538 assert_eq!(bus.stats().subscriber_count, 1);
539 bus.unsubscribe(id2);
540 assert_eq!(bus.stats().subscriber_count, 0);
541 }
542}