1use std::net::SocketAddr;
38
39use crate::nat_traversal_api::PeerId;
40use crate::node_status::NatType;
41use crate::transport::TransportAddr;
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum DisconnectReason {
46 Graceful,
48 Timeout,
50 Reset,
52 ApplicationClose,
54 Idle,
56 TransportError(String),
58 Unknown,
60}
61
62impl std::fmt::Display for DisconnectReason {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Self::Graceful => write!(f, "graceful shutdown"),
66 Self::Timeout => write!(f, "connection timeout"),
67 Self::Reset => write!(f, "connection reset"),
68 Self::ApplicationClose => write!(f, "application close"),
69 Self::Idle => write!(f, "idle timeout"),
70 Self::TransportError(e) => write!(f, "transport error: {}", e),
71 Self::Unknown => write!(f, "unknown reason"),
72 }
73 }
74}
75
76#[derive(Debug, Clone)]
81pub enum NodeEvent {
82 PeerConnected {
85 peer_id: PeerId,
87 addr: TransportAddr,
89 direct: bool,
91 },
92
93 PeerDisconnected {
95 peer_id: PeerId,
97 reason: DisconnectReason,
99 },
100
101 ConnectionFailed {
103 addr: SocketAddr,
105 error: String,
107 },
108
109 ExternalAddressDiscovered {
114 addr: TransportAddr,
116 },
117
118 NatTypeDetected {
120 nat_type: NatType,
122 },
123
124 NatTraversalComplete {
126 peer_id: PeerId,
128 success: bool,
130 method: TraversalMethod,
132 },
133
134 RelaySessionStarted {
137 peer_id: PeerId,
139 },
140
141 RelaySessionEnded {
143 peer_id: PeerId,
145 bytes_forwarded: u64,
147 },
148
149 CoordinationStarted {
152 peer_a: PeerId,
154 peer_b: PeerId,
156 },
157
158 CoordinationComplete {
160 peer_a: PeerId,
162 peer_b: PeerId,
164 success: bool,
166 },
167
168 DataReceived {
171 peer_id: PeerId,
173 stream_id: u64,
175 bytes: usize,
177 },
178
179 DataSent {
181 peer_id: PeerId,
183 stream_id: u64,
185 bytes: usize,
187 },
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
192pub enum TraversalMethod {
193 Direct,
195 HolePunch,
197 Relay,
199 PortPrediction,
201}
202
203impl std::fmt::Display for TraversalMethod {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 match self {
206 Self::Direct => write!(f, "direct"),
207 Self::HolePunch => write!(f, "hole punch"),
208 Self::Relay => write!(f, "relay"),
209 Self::PortPrediction => write!(f, "port prediction"),
210 }
211 }
212}
213
214impl NodeEvent {
215 pub fn is_connection_event(&self) -> bool {
217 matches!(
218 self,
219 Self::PeerConnected { .. }
220 | Self::PeerDisconnected { .. }
221 | Self::ConnectionFailed { .. }
222 )
223 }
224
225 pub fn is_nat_event(&self) -> bool {
227 matches!(
228 self,
229 Self::ExternalAddressDiscovered { .. }
230 | Self::NatTypeDetected { .. }
231 | Self::NatTraversalComplete { .. }
232 )
233 }
234
235 pub fn is_relay_event(&self) -> bool {
237 matches!(
238 self,
239 Self::RelaySessionStarted { .. } | Self::RelaySessionEnded { .. }
240 )
241 }
242
243 pub fn is_coordination_event(&self) -> bool {
245 matches!(
246 self,
247 Self::CoordinationStarted { .. } | Self::CoordinationComplete { .. }
248 )
249 }
250
251 pub fn is_data_event(&self) -> bool {
253 matches!(self, Self::DataReceived { .. } | Self::DataSent { .. })
254 }
255
256 pub fn peer_id(&self) -> Option<&PeerId> {
258 match self {
259 Self::PeerConnected { peer_id, .. } => Some(peer_id),
260 Self::PeerDisconnected { peer_id, .. } => Some(peer_id),
261 Self::NatTraversalComplete { peer_id, .. } => Some(peer_id),
262 Self::RelaySessionStarted { peer_id } => Some(peer_id),
263 Self::RelaySessionEnded { peer_id, .. } => Some(peer_id),
264 Self::DataReceived { peer_id, .. } => Some(peer_id),
265 Self::DataSent { peer_id, .. } => Some(peer_id),
266 _ => None,
267 }
268 }
269}
270
271use crate::p2p_endpoint::DisconnectReason as P2pDisconnectReason;
273
274impl From<P2pDisconnectReason> for DisconnectReason {
279 fn from(reason: P2pDisconnectReason) -> Self {
280 match reason {
281 P2pDisconnectReason::Normal => Self::Graceful,
282 P2pDisconnectReason::Timeout => Self::Timeout,
283 P2pDisconnectReason::ProtocolError(e) => Self::TransportError(e),
284 P2pDisconnectReason::AuthenticationFailed => {
285 Self::TransportError("authentication failed".to_string())
286 }
287 P2pDisconnectReason::ConnectionLost => Self::Reset,
288 P2pDisconnectReason::RemoteClosed => Self::ApplicationClose,
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 fn test_peer_id() -> PeerId {
298 PeerId([1u8; 32])
299 }
300
301 fn test_addr() -> SocketAddr {
302 "127.0.0.1:9000".parse().unwrap()
303 }
304
305 #[test]
306 fn test_peer_connected_event() {
307 let event = NodeEvent::PeerConnected {
308 peer_id: test_peer_id(),
309 addr: TransportAddr::Udp(test_addr()),
310 direct: true,
311 };
312
313 assert!(event.is_connection_event());
314 assert!(!event.is_nat_event());
315 assert_eq!(event.peer_id(), Some(&test_peer_id()));
316 }
317
318 #[test]
319 fn test_peer_disconnected_event() {
320 let event = NodeEvent::PeerDisconnected {
321 peer_id: test_peer_id(),
322 reason: DisconnectReason::Graceful,
323 };
324
325 assert!(event.is_connection_event());
326 assert_eq!(event.peer_id(), Some(&test_peer_id()));
327 }
328
329 #[test]
330 fn test_nat_type_detected_event() {
331 let event = NodeEvent::NatTypeDetected {
332 nat_type: NatType::FullCone,
333 };
334
335 assert!(event.is_nat_event());
336 assert!(!event.is_connection_event());
337 assert!(event.peer_id().is_none());
338 }
339
340 #[test]
341 fn test_relay_session_events() {
342 let start = NodeEvent::RelaySessionStarted {
343 peer_id: test_peer_id(),
344 };
345
346 let end = NodeEvent::RelaySessionEnded {
347 peer_id: test_peer_id(),
348 bytes_forwarded: 1024,
349 };
350
351 assert!(start.is_relay_event());
352 assert!(end.is_relay_event());
353 assert!(!start.is_connection_event());
354 }
355
356 #[test]
357 fn test_coordination_events() {
358 let peer_a = PeerId([1u8; 32]);
359 let peer_b = PeerId([2u8; 32]);
360
361 let start = NodeEvent::CoordinationStarted { peer_a, peer_b };
362
363 let complete = NodeEvent::CoordinationComplete {
364 peer_a,
365 peer_b,
366 success: true,
367 };
368
369 assert!(start.is_coordination_event());
370 assert!(complete.is_coordination_event());
371 }
372
373 #[test]
374 fn test_data_events() {
375 let recv = NodeEvent::DataReceived {
376 peer_id: test_peer_id(),
377 stream_id: 1,
378 bytes: 1024,
379 };
380
381 let send = NodeEvent::DataSent {
382 peer_id: test_peer_id(),
383 stream_id: 1,
384 bytes: 512,
385 };
386
387 assert!(recv.is_data_event());
388 assert!(send.is_data_event());
389 assert!(!recv.is_connection_event());
390 }
391
392 #[test]
393 fn test_disconnect_reason_display() {
394 assert_eq!(
395 format!("{}", DisconnectReason::Graceful),
396 "graceful shutdown"
397 );
398 assert_eq!(
399 format!("{}", DisconnectReason::Timeout),
400 "connection timeout"
401 );
402 assert_eq!(
403 format!("{}", DisconnectReason::TransportError("test".to_string())),
404 "transport error: test"
405 );
406 }
407
408 #[test]
409 fn test_traversal_method_display() {
410 assert_eq!(format!("{}", TraversalMethod::Direct), "direct");
411 assert_eq!(format!("{}", TraversalMethod::HolePunch), "hole punch");
412 assert_eq!(format!("{}", TraversalMethod::Relay), "relay");
413 assert_eq!(
414 format!("{}", TraversalMethod::PortPrediction),
415 "port prediction"
416 );
417 }
418
419 #[test]
420 fn test_events_are_clone() {
421 let event = NodeEvent::PeerConnected {
422 peer_id: test_peer_id(),
423 addr: TransportAddr::Udp(test_addr()),
424 direct: true,
425 };
426
427 let cloned = event.clone();
428 assert!(cloned.is_connection_event());
429 }
430
431 #[test]
432 fn test_events_are_debug() {
433 let event = NodeEvent::NatTypeDetected {
434 nat_type: NatType::Symmetric,
435 };
436
437 let debug_str = format!("{:?}", event);
438 assert!(debug_str.contains("NatTypeDetected"));
439 assert!(debug_str.contains("Symmetric"));
440 }
441
442 #[test]
443 fn test_connection_failed_event() {
444 let event = NodeEvent::ConnectionFailed {
445 addr: test_addr(),
446 error: "connection refused".to_string(),
447 };
448
449 assert!(event.is_connection_event());
450 assert!(event.peer_id().is_none());
451 }
452
453 #[test]
454 fn test_external_address_discovered() {
455 let event = NodeEvent::ExternalAddressDiscovered {
456 addr: TransportAddr::Udp("1.2.3.4:9000".parse().unwrap()),
457 };
458
459 assert!(event.is_nat_event());
460 assert!(event.peer_id().is_none());
461 }
462}