1use std::net::SocketAddr;
38
39use crate::nat_traversal_api::PeerId;
40use crate::node_status::NatType;
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum DisconnectReason {
45 Graceful,
47 Timeout,
49 Reset,
51 ApplicationClose,
53 Idle,
55 TransportError(String),
57 Unknown,
59}
60
61impl std::fmt::Display for DisconnectReason {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::Graceful => write!(f, "graceful shutdown"),
65 Self::Timeout => write!(f, "connection timeout"),
66 Self::Reset => write!(f, "connection reset"),
67 Self::ApplicationClose => write!(f, "application close"),
68 Self::Idle => write!(f, "idle timeout"),
69 Self::TransportError(e) => write!(f, "transport error: {}", e),
70 Self::Unknown => write!(f, "unknown reason"),
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
80pub enum NodeEvent {
81 PeerConnected {
84 peer_id: PeerId,
86 addr: SocketAddr,
88 direct: bool,
90 },
91
92 PeerDisconnected {
94 peer_id: PeerId,
96 reason: DisconnectReason,
98 },
99
100 ConnectionFailed {
102 addr: SocketAddr,
104 error: String,
106 },
107
108 ExternalAddressDiscovered {
113 addr: SocketAddr,
115 },
116
117 NatTypeDetected {
119 nat_type: NatType,
121 },
122
123 NatTraversalComplete {
125 peer_id: PeerId,
127 success: bool,
129 method: TraversalMethod,
131 },
132
133 RelaySessionStarted {
136 peer_id: PeerId,
138 },
139
140 RelaySessionEnded {
142 peer_id: PeerId,
144 bytes_forwarded: u64,
146 },
147
148 CoordinationStarted {
151 peer_a: PeerId,
153 peer_b: PeerId,
155 },
156
157 CoordinationComplete {
159 peer_a: PeerId,
161 peer_b: PeerId,
163 success: bool,
165 },
166
167 DataReceived {
170 peer_id: PeerId,
172 stream_id: u64,
174 bytes: usize,
176 },
177
178 DataSent {
180 peer_id: PeerId,
182 stream_id: u64,
184 bytes: usize,
186 },
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum TraversalMethod {
192 Direct,
194 HolePunch,
196 Relay,
198 PortPrediction,
200}
201
202impl std::fmt::Display for TraversalMethod {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 match self {
205 Self::Direct => write!(f, "direct"),
206 Self::HolePunch => write!(f, "hole punch"),
207 Self::Relay => write!(f, "relay"),
208 Self::PortPrediction => write!(f, "port prediction"),
209 }
210 }
211}
212
213impl NodeEvent {
214 pub fn is_connection_event(&self) -> bool {
216 matches!(
217 self,
218 Self::PeerConnected { .. }
219 | Self::PeerDisconnected { .. }
220 | Self::ConnectionFailed { .. }
221 )
222 }
223
224 pub fn is_nat_event(&self) -> bool {
226 matches!(
227 self,
228 Self::ExternalAddressDiscovered { .. }
229 | Self::NatTypeDetected { .. }
230 | Self::NatTraversalComplete { .. }
231 )
232 }
233
234 pub fn is_relay_event(&self) -> bool {
236 matches!(
237 self,
238 Self::RelaySessionStarted { .. } | Self::RelaySessionEnded { .. }
239 )
240 }
241
242 pub fn is_coordination_event(&self) -> bool {
244 matches!(
245 self,
246 Self::CoordinationStarted { .. } | Self::CoordinationComplete { .. }
247 )
248 }
249
250 pub fn is_data_event(&self) -> bool {
252 matches!(self, Self::DataReceived { .. } | Self::DataSent { .. })
253 }
254
255 pub fn peer_id(&self) -> Option<&PeerId> {
257 match self {
258 Self::PeerConnected { peer_id, .. } => Some(peer_id),
259 Self::PeerDisconnected { peer_id, .. } => Some(peer_id),
260 Self::NatTraversalComplete { peer_id, .. } => Some(peer_id),
261 Self::RelaySessionStarted { peer_id } => Some(peer_id),
262 Self::RelaySessionEnded { peer_id, .. } => Some(peer_id),
263 Self::DataReceived { peer_id, .. } => Some(peer_id),
264 Self::DataSent { peer_id, .. } => Some(peer_id),
265 _ => None,
266 }
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 fn test_peer_id() -> PeerId {
275 PeerId([1u8; 32])
276 }
277
278 fn test_addr() -> SocketAddr {
279 "127.0.0.1:9000".parse().unwrap()
280 }
281
282 #[test]
283 fn test_peer_connected_event() {
284 let event = NodeEvent::PeerConnected {
285 peer_id: test_peer_id(),
286 addr: test_addr(),
287 direct: true,
288 };
289
290 assert!(event.is_connection_event());
291 assert!(!event.is_nat_event());
292 assert_eq!(event.peer_id(), Some(&test_peer_id()));
293 }
294
295 #[test]
296 fn test_peer_disconnected_event() {
297 let event = NodeEvent::PeerDisconnected {
298 peer_id: test_peer_id(),
299 reason: DisconnectReason::Graceful,
300 };
301
302 assert!(event.is_connection_event());
303 assert_eq!(event.peer_id(), Some(&test_peer_id()));
304 }
305
306 #[test]
307 fn test_nat_type_detected_event() {
308 let event = NodeEvent::NatTypeDetected {
309 nat_type: NatType::FullCone,
310 };
311
312 assert!(event.is_nat_event());
313 assert!(!event.is_connection_event());
314 assert!(event.peer_id().is_none());
315 }
316
317 #[test]
318 fn test_relay_session_events() {
319 let start = NodeEvent::RelaySessionStarted {
320 peer_id: test_peer_id(),
321 };
322
323 let end = NodeEvent::RelaySessionEnded {
324 peer_id: test_peer_id(),
325 bytes_forwarded: 1024,
326 };
327
328 assert!(start.is_relay_event());
329 assert!(end.is_relay_event());
330 assert!(!start.is_connection_event());
331 }
332
333 #[test]
334 fn test_coordination_events() {
335 let peer_a = PeerId([1u8; 32]);
336 let peer_b = PeerId([2u8; 32]);
337
338 let start = NodeEvent::CoordinationStarted { peer_a, peer_b };
339
340 let complete = NodeEvent::CoordinationComplete {
341 peer_a,
342 peer_b,
343 success: true,
344 };
345
346 assert!(start.is_coordination_event());
347 assert!(complete.is_coordination_event());
348 }
349
350 #[test]
351 fn test_data_events() {
352 let recv = NodeEvent::DataReceived {
353 peer_id: test_peer_id(),
354 stream_id: 1,
355 bytes: 1024,
356 };
357
358 let send = NodeEvent::DataSent {
359 peer_id: test_peer_id(),
360 stream_id: 1,
361 bytes: 512,
362 };
363
364 assert!(recv.is_data_event());
365 assert!(send.is_data_event());
366 assert!(!recv.is_connection_event());
367 }
368
369 #[test]
370 fn test_disconnect_reason_display() {
371 assert_eq!(
372 format!("{}", DisconnectReason::Graceful),
373 "graceful shutdown"
374 );
375 assert_eq!(
376 format!("{}", DisconnectReason::Timeout),
377 "connection timeout"
378 );
379 assert_eq!(
380 format!("{}", DisconnectReason::TransportError("test".to_string())),
381 "transport error: test"
382 );
383 }
384
385 #[test]
386 fn test_traversal_method_display() {
387 assert_eq!(format!("{}", TraversalMethod::Direct), "direct");
388 assert_eq!(format!("{}", TraversalMethod::HolePunch), "hole punch");
389 assert_eq!(format!("{}", TraversalMethod::Relay), "relay");
390 assert_eq!(
391 format!("{}", TraversalMethod::PortPrediction),
392 "port prediction"
393 );
394 }
395
396 #[test]
397 fn test_events_are_clone() {
398 let event = NodeEvent::PeerConnected {
399 peer_id: test_peer_id(),
400 addr: test_addr(),
401 direct: true,
402 };
403
404 let cloned = event.clone();
405 assert!(cloned.is_connection_event());
406 }
407
408 #[test]
409 fn test_events_are_debug() {
410 let event = NodeEvent::NatTypeDetected {
411 nat_type: NatType::Symmetric,
412 };
413
414 let debug_str = format!("{:?}", event);
415 assert!(debug_str.contains("NatTypeDetected"));
416 assert!(debug_str.contains("Symmetric"));
417 }
418
419 #[test]
420 fn test_connection_failed_event() {
421 let event = NodeEvent::ConnectionFailed {
422 addr: test_addr(),
423 error: "connection refused".to_string(),
424 };
425
426 assert!(event.is_connection_event());
427 assert!(event.peer_id().is_none());
428 }
429
430 #[test]
431 fn test_external_address_discovered() {
432 let event = NodeEvent::ExternalAddressDiscovered {
433 addr: "1.2.3.4:9000".parse().unwrap(),
434 };
435
436 assert!(event.is_nat_event());
437 assert!(event.peer_id().is_none());
438 }
439}