1use std::{
11 fmt,
12 net::SocketAddr,
13 sync::{Arc, Mutex},
14};
15
16use crate::{connection::PathStats, high_level::WeakConnectionHandle};
17
18#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct PathId(u64);
24
25impl PathId {
26 pub const PRIMARY: Self = Self(0);
28
29 pub const fn get(self) -> u64 {
31 self.0
32 }
33}
34
35impl From<u64> for PathId {
36 fn from(value: u64) -> Self {
37 Self(value)
38 }
39}
40
41impl From<PathId> for u64 {
42 fn from(value: PathId) -> Self {
43 value.0
44 }
45}
46
47impl fmt::Display for PathId {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 self.0.fmt(f)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::connection::PathStats;
57
58 #[test]
61 fn path_id_primary_is_zero() {
62 assert_eq!(PathId::PRIMARY.get(), 0);
63 }
64
65 #[test]
66 fn path_id_from_u64() {
67 let id: PathId = 42u64.into();
68 assert_eq!(id.get(), 42);
69 }
70
71 #[test]
72 fn path_id_into_u64() {
73 let id = PathId::PRIMARY;
74 let val: u64 = id.into();
75 assert_eq!(val, 0);
76 }
77
78 #[test]
79 fn path_id_default_is_zero() {
80 let id = PathId::default();
81 assert_eq!(id.get(), 0);
82 assert_eq!(id, PathId::PRIMARY);
83 }
84
85 #[test]
86 fn path_id_equality() {
87 assert_eq!(PathId::from(1u64), PathId::from(1u64));
88 assert_ne!(PathId::from(1u64), PathId::from(2u64));
89 }
90
91 #[test]
92 fn path_id_ordering() {
93 assert!(PathId::from(0u64) < PathId::from(1u64));
94 assert!(PathId::from(1u64) > PathId::from(0u64));
95 }
96
97 #[test]
98 fn path_id_display() {
99 assert_eq!(format!("{}", PathId::PRIMARY), "0");
100 assert_eq!(format!("{}", PathId::from(42u64)), "42");
101 }
102
103 #[test]
104 fn path_id_debug() {
105 let debug = format!("{:?}", PathId::from(42u64));
106 assert!(debug.contains("42"));
107 }
108
109 #[test]
110 fn path_id_clone() {
111 let a = PathId::from(5u64);
112 let b = a;
113 assert_eq!(a, b);
114 }
115
116 #[test]
117 fn path_id_hash() {
118 use std::collections::hash_map::DefaultHasher;
119 use std::hash::{Hash, Hasher};
120
121 let a = PathId::from(10u64);
122 let b = PathId::from(10u64);
123 let mut ha = DefaultHasher::new();
124 let mut hb = DefaultHasher::new();
125 a.hash(&mut ha);
126 b.hash(&mut hb);
127 assert_eq!(ha.finish(), hb.finish());
128 }
129
130 #[test]
133 fn path_snapshot_default() {
134 let stats = PathStats::default();
135 let snapshot = PathSnapshot {
137 stats,
138 remote_address: "127.0.0.1:9000".parse().unwrap(),
139 observed_external_addr: None,
140 };
141 assert_eq!(snapshot.remote_address.port(), 9000);
142 assert!(snapshot.observed_external_addr.is_none());
143 }
144
145 #[test]
146 fn path_snapshot_with_external_addr() {
147 let stats = PathStats::default();
148 let snapshot = PathSnapshot {
149 stats,
150 remote_address: "192.168.1.1:9000".parse().unwrap(),
151 observed_external_addr: Some("10.0.0.1:9001".parse().unwrap()),
152 };
153 assert!(snapshot.observed_external_addr.is_some());
154 assert_eq!(
155 snapshot.observed_external_addr.unwrap().to_string(),
156 "10.0.0.1:9001"
157 );
158 }
159
160 #[test]
161 fn path_snapshot_clone_copy() {
162 let stats = PathStats::default();
163 let s1 = PathSnapshot {
164 stats,
165 remote_address: "127.0.0.1:80".parse().unwrap(),
166 observed_external_addr: None,
167 };
168 let s2 = s1;
169 assert_eq!(s1.remote_address, s2.remote_address);
170 }
171
172 #[test]
175 fn retained_path_snapshot_store_and_load() {
176 let stats = PathStats::default();
177 let snapshot = PathSnapshot {
178 stats,
179 remote_address: "127.0.0.1:9000".parse().unwrap(),
180 observed_external_addr: None,
181 };
182 let retained = RetainedPathSnapshot::new(snapshot);
183 let loaded = retained.load();
184 assert_eq!(loaded.remote_address.port(), 9000);
185 }
186
187 #[test]
188 fn retained_path_snapshot_overwrite() {
189 let stats = PathStats::default();
190 let snapshot1 = PathSnapshot {
191 stats,
192 remote_address: "127.0.0.1:1".parse().unwrap(),
193 observed_external_addr: None,
194 };
195 let retained = RetainedPathSnapshot::new(snapshot1);
196
197 let snapshot2 = PathSnapshot {
198 stats: PathStats::default(),
199 remote_address: "127.0.0.1:2".parse().unwrap(),
200 observed_external_addr: None,
201 };
202 retained.store(snapshot2);
203
204 let loaded = retained.load();
205 assert_eq!(loaded.remote_address.port(), 2);
206 }
207
208 #[test]
209 fn retained_path_snapshot_clone() {
210 let stats = PathStats::default();
211 let snapshot = PathSnapshot {
212 stats,
213 remote_address: "10.0.0.1:8000".parse().unwrap(),
214 observed_external_addr: None,
215 };
216 let retained = RetainedPathSnapshot::new(snapshot);
217 let cloned = retained.clone();
218 assert_eq!(retained.load().remote_address, cloned.load().remote_address);
219 }
220
221 #[test]
222 fn retained_path_snapshot_concurrent_independence() {
223 let stats = PathStats::default();
224 let snapshot = PathSnapshot {
225 stats,
226 remote_address: "127.0.0.1:9000".parse().unwrap(),
227 observed_external_addr: None,
228 };
229 let retained = RetainedPathSnapshot::new(snapshot);
230 let cloned = retained.clone();
231
232 let new_snapshot = PathSnapshot {
234 stats: PathStats::default(),
235 remote_address: "127.0.0.1:9999".parse().unwrap(),
236 observed_external_addr: None,
237 };
238 retained.store(new_snapshot);
239
240 assert_eq!(retained.load().remote_address.port(), 9999);
241 assert_eq!(cloned.load().remote_address.port(), 9999); }
243}
244
245#[derive(Debug, Clone, Copy)]
247pub(crate) struct PathSnapshot {
248 pub(crate) stats: PathStats,
249 pub(crate) remote_address: SocketAddr,
250 pub(crate) observed_external_addr: Option<SocketAddr>,
251}
252
253#[derive(Debug, Clone)]
254struct RetainedPathSnapshot(Arc<Mutex<PathSnapshot>>);
255
256impl RetainedPathSnapshot {
257 fn new(snapshot: PathSnapshot) -> Self {
258 Self(Arc::new(Mutex::new(snapshot)))
259 }
260
261 fn load(&self) -> PathSnapshot {
262 match self.0.lock() {
263 Ok(snapshot) => *snapshot,
264 Err(poisoned) => *poisoned.into_inner(),
265 }
266 }
267
268 fn store(&self, snapshot: PathSnapshot) {
269 match self.0.lock() {
270 Ok(mut retained) => *retained = snapshot,
271 Err(poisoned) => *poisoned.into_inner() = snapshot,
272 }
273 }
274}
275
276#[derive(Debug, Clone)]
282pub struct Path {
283 conn_handle: WeakConnectionHandle,
284 id: PathId,
285 retained: RetainedPathSnapshot,
286}
287
288impl Path {
289 pub(crate) fn new(
290 conn_handle: WeakConnectionHandle,
291 id: PathId,
292 snapshot: PathSnapshot,
293 ) -> Self {
294 Self {
295 conn_handle,
296 id,
297 retained: RetainedPathSnapshot::new(snapshot),
298 }
299 }
300
301 fn live_snapshot(&self) -> Option<PathSnapshot> {
302 self.conn_handle
303 .upgrade()
304 .and_then(|conn| conn.path_snapshot(self.id))
305 }
306
307 fn snapshot(&self) -> PathSnapshot {
308 if let Some(snapshot) = self.live_snapshot() {
309 self.retained.store(snapshot);
310 snapshot
311 } else {
312 self.retained.load()
313 }
314 }
315
316 pub fn id(&self) -> PathId {
318 self.id
319 }
320
321 pub fn stats(&self) -> PathStats {
323 self.snapshot().stats
324 }
325
326 pub fn remote_address(&self) -> SocketAddr {
328 self.snapshot().remote_address
329 }
330
331 pub fn observed_external_addr(&self) -> Option<SocketAddr> {
333 self.snapshot().observed_external_addr
334 }
335
336 pub fn weak_handle(&self) -> WeakPathHandle {
338 WeakPathHandle {
339 conn_handle: self.conn_handle.clone(),
340 id: self.id,
341 retained: self.retained.clone(),
342 }
343 }
344}
345
346impl Drop for Path {
347 fn drop(&mut self) {
348 if let Some(snapshot) = self.live_snapshot() {
349 self.retained.store(snapshot);
350 }
351 }
352}
353
354#[derive(Debug, Clone)]
359pub struct WeakPathHandle {
360 conn_handle: WeakConnectionHandle,
361 id: PathId,
362 retained: RetainedPathSnapshot,
363}
364
365impl WeakPathHandle {
366 fn live_snapshot(&self) -> Option<PathSnapshot> {
367 self.conn_handle
368 .upgrade()
369 .and_then(|conn| conn.path_snapshot(self.id))
370 }
371
372 fn snapshot(&self) -> PathSnapshot {
373 if let Some(snapshot) = self.live_snapshot() {
374 self.retained.store(snapshot);
375 snapshot
376 } else {
377 self.retained.load()
378 }
379 }
380
381 pub fn id(&self) -> PathId {
383 self.id
384 }
385
386 pub fn upgrade(&self) -> Option<Path> {
388 if !self.conn_handle.is_alive() {
389 return None;
390 }
391
392 let snapshot = self.live_snapshot()?;
393 Some(Path {
394 conn_handle: self.conn_handle.clone(),
395 id: self.id,
396 retained: RetainedPathSnapshot::new(snapshot),
397 })
398 }
399
400 pub fn is_alive(&self) -> bool {
402 self.conn_handle.is_alive() && self.live_snapshot().is_some()
403 }
404
405 pub fn stats(&self) -> PathStats {
407 self.snapshot().stats
408 }
409
410 pub fn remote_address(&self) -> SocketAddr {
412 self.snapshot().remote_address
413 }
414
415 pub fn observed_external_addr(&self) -> Option<SocketAddr> {
417 self.snapshot().observed_external_addr
418 }
419}