1use std::net::SocketAddr;
39
40use crate::mdns::MdnsPeerRecord;
41use crate::nat_traversal_api::PeerId;
42use crate::node_status::NatType;
43pub use crate::p2p_endpoint::{DirectPathStatus, DirectPathUnavailableReason};
44pub use crate::reachability::TraversalMethod;
45use crate::transport::TransportAddr;
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum DisconnectReason {
50 Graceful,
52 Timeout,
54 Reset,
56 ApplicationClose,
58 Idle,
60 TransportError(String),
62 Unknown,
64}
65
66impl std::fmt::Display for DisconnectReason {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 Self::Graceful => write!(f, "graceful shutdown"),
70 Self::Timeout => write!(f, "connection timeout"),
71 Self::Reset => write!(f, "connection reset"),
72 Self::ApplicationClose => write!(f, "application close"),
73 Self::Idle => write!(f, "idle timeout"),
74 Self::TransportError(e) => write!(f, "transport error: {}", e),
75 Self::Unknown => write!(f, "unknown reason"),
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
85pub enum NodeEvent {
86 PeerConnected {
89 peer_id: PeerId,
91 addr: TransportAddr,
93 method: TraversalMethod,
95 direct: bool,
97 },
98
99 PeerDisconnected {
101 peer_id: PeerId,
103 reason: DisconnectReason,
105 },
106
107 ConnectionFailed {
109 addr: SocketAddr,
111 error: String,
113 },
114
115 ExternalAddressDiscovered {
120 addr: TransportAddr,
122 },
123
124 PortMappingEstablished {
126 external_addr: SocketAddr,
128 },
129
130 PortMappingRenewed {
132 external_addr: SocketAddr,
134 },
135
136 PortMappingAddressChanged {
138 previous_addr: SocketAddr,
140 external_addr: SocketAddr,
142 },
143
144 PortMappingFailed {
146 error: String,
148 },
149
150 PortMappingRemoved {
152 external_addr: Option<SocketAddr>,
154 },
155
156 NatTypeDetected {
158 nat_type: NatType,
161 },
162
163 NatTraversalComplete {
165 peer_id: PeerId,
167 success: bool,
169 method: TraversalMethod,
171 },
172
173 DirectPathStatus {
175 peer_id: PeerId,
177 status: DirectPathStatus,
179 },
180
181 RelaySessionStarted {
184 peer_id: PeerId,
186 },
187
188 RelaySessionEnded {
190 peer_id: PeerId,
192 bytes_forwarded: u64,
194 },
195
196 CoordinationStarted {
199 peer_a: PeerId,
201 peer_b: PeerId,
203 },
204
205 CoordinationComplete {
207 peer_a: PeerId,
209 peer_b: PeerId,
211 success: bool,
213 },
214
215 MdnsServiceAdvertised {
218 service: String,
220 namespace: Option<String>,
222 instance_fullname: String,
224 },
225
226 MdnsPeerDiscovered {
228 peer: MdnsPeerRecord,
230 },
231
232 MdnsPeerUpdated {
234 peer: MdnsPeerRecord,
236 },
237
238 MdnsPeerRemoved {
240 peer: MdnsPeerRecord,
242 },
243
244 MdnsPeerEligible {
246 peer: MdnsPeerRecord,
248 },
249
250 MdnsPeerIneligible {
252 peer: MdnsPeerRecord,
254 reason: String,
256 },
257
258 MdnsPeerApprovalRequired {
260 peer: MdnsPeerRecord,
262 reason: String,
264 },
265
266 MdnsAutoConnectAttempted {
268 peer: MdnsPeerRecord,
270 addresses: Vec<SocketAddr>,
272 },
273
274 MdnsAutoConnectSucceeded {
276 peer: MdnsPeerRecord,
278 authenticated_peer_id: PeerId,
280 remote_addr: TransportAddr,
282 },
283
284 MdnsAutoConnectFailed {
286 peer: MdnsPeerRecord,
288 addresses: Vec<SocketAddr>,
290 error: String,
292 },
293
294 DataReceived {
297 peer_id: PeerId,
299 stream_id: u64,
301 bytes: usize,
303 },
304
305 DataSent {
307 peer_id: PeerId,
309 stream_id: u64,
311 bytes: usize,
313 },
314}
315
316impl NodeEvent {
317 pub fn is_connection_event(&self) -> bool {
319 matches!(
320 self,
321 Self::PeerConnected { .. }
322 | Self::PeerDisconnected { .. }
323 | Self::ConnectionFailed { .. }
324 )
325 }
326
327 pub fn is_nat_event(&self) -> bool {
329 matches!(
330 self,
331 Self::ExternalAddressDiscovered { .. }
332 | Self::PortMappingEstablished { .. }
333 | Self::PortMappingRenewed { .. }
334 | Self::PortMappingAddressChanged { .. }
335 | Self::PortMappingFailed { .. }
336 | Self::PortMappingRemoved { .. }
337 | Self::NatTypeDetected { .. }
338 | Self::NatTraversalComplete { .. }
339 | Self::DirectPathStatus { .. }
340 )
341 }
342
343 pub fn is_relay_event(&self) -> bool {
345 matches!(
346 self,
347 Self::RelaySessionStarted { .. } | Self::RelaySessionEnded { .. }
348 )
349 }
350
351 pub fn is_coordination_event(&self) -> bool {
353 matches!(
354 self,
355 Self::CoordinationStarted { .. } | Self::CoordinationComplete { .. }
356 )
357 }
358
359 pub fn is_data_event(&self) -> bool {
361 matches!(self, Self::DataReceived { .. } | Self::DataSent { .. })
362 }
363
364 pub fn peer_id(&self) -> Option<&PeerId> {
366 match self {
367 Self::PeerConnected { peer_id, .. } => Some(peer_id),
368 Self::PeerDisconnected { peer_id, .. } => Some(peer_id),
369 Self::NatTraversalComplete { peer_id, .. } => Some(peer_id),
370 Self::DirectPathStatus { peer_id, .. } => Some(peer_id),
371 Self::RelaySessionStarted { peer_id } => Some(peer_id),
372 Self::RelaySessionEnded { peer_id, .. } => Some(peer_id),
373 Self::DataReceived { peer_id, .. } => Some(peer_id),
374 Self::DataSent { peer_id, .. } => Some(peer_id),
375 Self::MdnsAutoConnectSucceeded {
376 authenticated_peer_id,
377 ..
378 } => Some(authenticated_peer_id),
379 _ => None,
380 }
381 }
382}
383
384use crate::p2p_endpoint::DisconnectReason as P2pDisconnectReason;
386
387impl From<P2pDisconnectReason> for DisconnectReason {
392 fn from(reason: P2pDisconnectReason) -> Self {
393 match reason {
394 P2pDisconnectReason::Normal => Self::Graceful,
395 P2pDisconnectReason::Timeout => Self::Timeout,
396 P2pDisconnectReason::ProtocolError(e) => Self::TransportError(e),
397 P2pDisconnectReason::AuthenticationFailed => {
398 Self::TransportError("authentication failed".to_string())
399 }
400 P2pDisconnectReason::ConnectionLost => Self::Reset,
401 P2pDisconnectReason::RemoteClosed => Self::ApplicationClose,
402 P2pDisconnectReason::LivenessTimeout => Self::Timeout,
408 }
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415
416 fn test_peer_id() -> PeerId {
417 PeerId([1u8; 32])
418 }
419
420 fn test_addr() -> SocketAddr {
421 "127.0.0.1:9000".parse().unwrap()
422 }
423
424 #[test]
425 fn test_peer_connected_event() {
426 let event = NodeEvent::PeerConnected {
427 peer_id: test_peer_id(),
428 addr: TransportAddr::Udp(test_addr()),
429 method: TraversalMethod::Direct,
430 direct: true,
431 };
432
433 assert!(event.is_connection_event());
434 assert!(!event.is_nat_event());
435 assert_eq!(event.peer_id(), Some(&test_peer_id()));
436 }
437
438 #[test]
439 fn test_peer_disconnected_event() {
440 let event = NodeEvent::PeerDisconnected {
441 peer_id: test_peer_id(),
442 reason: DisconnectReason::Graceful,
443 };
444
445 assert!(event.is_connection_event());
446 assert_eq!(event.peer_id(), Some(&test_peer_id()));
447 }
448
449 #[test]
450 fn test_nat_type_detected_event() {
451 let event = NodeEvent::NatTypeDetected {
452 nat_type: NatType::FullCone,
453 };
454
455 assert!(event.is_nat_event());
456 assert!(!event.is_connection_event());
457 assert!(event.peer_id().is_none());
458 }
459
460 #[test]
461 fn test_direct_path_status_event() {
462 let event = NodeEvent::DirectPathStatus {
463 peer_id: test_peer_id(),
464 status: DirectPathStatus::BestEffortUnavailable {
465 reason: DirectPathUnavailableReason::NatUnreachable,
466 },
467 };
468
469 assert!(event.is_nat_event());
470 assert_eq!(event.peer_id(), Some(&test_peer_id()));
471 }
472
473 #[test]
474 fn test_relay_session_events() {
475 let start = NodeEvent::RelaySessionStarted {
476 peer_id: test_peer_id(),
477 };
478
479 let end = NodeEvent::RelaySessionEnded {
480 peer_id: test_peer_id(),
481 bytes_forwarded: 1024,
482 };
483
484 assert!(start.is_relay_event());
485 assert!(end.is_relay_event());
486 assert!(!start.is_connection_event());
487 }
488
489 #[test]
490 fn test_coordination_events() {
491 let peer_a = PeerId([1u8; 32]);
492 let peer_b = PeerId([2u8; 32]);
493
494 let start = NodeEvent::CoordinationStarted { peer_a, peer_b };
495
496 let complete = NodeEvent::CoordinationComplete {
497 peer_a,
498 peer_b,
499 success: true,
500 };
501
502 assert!(start.is_coordination_event());
503 assert!(complete.is_coordination_event());
504 }
505
506 #[test]
507 fn test_data_events() {
508 let recv = NodeEvent::DataReceived {
509 peer_id: test_peer_id(),
510 stream_id: 1,
511 bytes: 1024,
512 };
513
514 let send = NodeEvent::DataSent {
515 peer_id: test_peer_id(),
516 stream_id: 1,
517 bytes: 512,
518 };
519
520 assert!(recv.is_data_event());
521 assert!(send.is_data_event());
522 assert!(!recv.is_connection_event());
523 }
524
525 #[test]
526 fn test_disconnect_reason_display() {
527 assert_eq!(
528 format!("{}", DisconnectReason::Graceful),
529 "graceful shutdown"
530 );
531 assert_eq!(
532 format!("{}", DisconnectReason::Timeout),
533 "connection timeout"
534 );
535 assert_eq!(
536 format!("{}", DisconnectReason::TransportError("test".to_string())),
537 "transport error: test"
538 );
539 }
540
541 #[test]
542 fn test_traversal_method_display() {
543 assert_eq!(format!("{}", TraversalMethod::Direct), "direct");
544 assert_eq!(format!("{}", TraversalMethod::HolePunch), "hole punch");
545 assert_eq!(format!("{}", TraversalMethod::Relay), "relay");
546 assert_eq!(
547 format!("{}", TraversalMethod::PortPrediction),
548 "port prediction"
549 );
550 }
551
552 #[test]
553 fn test_events_are_clone() {
554 let event = NodeEvent::PeerConnected {
555 peer_id: test_peer_id(),
556 addr: TransportAddr::Udp(test_addr()),
557 method: TraversalMethod::Direct,
558 direct: true,
559 };
560
561 let cloned = event.clone();
562 assert!(cloned.is_connection_event());
563 }
564
565 #[test]
566 fn test_events_are_debug() {
567 let event = NodeEvent::NatTypeDetected {
568 nat_type: NatType::Symmetric,
569 };
570
571 let debug_str = format!("{:?}", event);
572 assert!(debug_str.contains("NatTypeDetected"));
573 assert!(debug_str.contains("Symmetric"));
574 }
575
576 #[test]
577 fn test_connection_failed_event() {
578 let event = NodeEvent::ConnectionFailed {
579 addr: test_addr(),
580 error: "connection refused".to_string(),
581 };
582
583 assert!(event.is_connection_event());
584 assert!(event.peer_id().is_none());
585 }
586
587 #[test]
588 fn test_external_address_discovered() {
589 let event = NodeEvent::ExternalAddressDiscovered {
590 addr: TransportAddr::Udp("1.2.3.4:9000".parse().unwrap()),
591 };
592
593 assert!(event.is_nat_event());
594 assert!(event.peer_id().is_none());
595 }
596}