Skip to main content

fips_core/transport/
handle.rs

1//! Enum wrapper for concrete transport implementations.
2
3#[cfg(target_os = "linux")]
4use super::ble::DefaultBleTransport;
5#[cfg(any(target_os = "linux", target_os = "macos"))]
6use super::ethernet::EthernetTransport;
7#[cfg(feature = "sim-transport")]
8use super::sim::SimTransport;
9use super::tcp::TcpTransport;
10use super::tor::TorTransport;
11use super::tor::control::TorMonitoringInfo;
12use super::udp::UdpTransport;
13#[cfg(feature = "webrtc-transport")]
14use super::webrtc::WebRtcTransport;
15use super::{
16    ConnectionState, DiscoveredPeer, Transport, TransportAddr, TransportCongestion, TransportError,
17    TransportId, TransportState, TransportType,
18};
19
20/// Wrapper enum for concrete transport implementations.
21///
22/// This enables polymorphic transport handling without trait objects,
23/// supporting async methods that the sync Transport trait cannot express.
24pub enum TransportHandle {
25    /// UDP/IP transport.
26    Udp(UdpTransport),
27    /// In-memory simulated packet transport.
28    #[cfg(feature = "sim-transport")]
29    Sim(SimTransport),
30    /// Raw Ethernet transport.
31    #[cfg(any(target_os = "linux", target_os = "macos"))]
32    Ethernet(EthernetTransport),
33    /// TCP/IP transport.
34    Tcp(TcpTransport),
35    /// Tor transport (via SOCKS5).
36    Tor(TorTransport),
37    /// WebRTC DataChannel transport.
38    #[cfg(feature = "webrtc-transport")]
39    WebRtc(Box<WebRtcTransport>),
40    /// BLE L2CAP transport.
41    #[cfg(target_os = "linux")]
42    Ble(DefaultBleTransport),
43}
44
45impl TransportHandle {
46    /// Start the transport asynchronously.
47    pub async fn start(&mut self) -> Result<(), TransportError> {
48        match self {
49            TransportHandle::Udp(t) => t.start_async().await,
50            #[cfg(feature = "sim-transport")]
51            TransportHandle::Sim(t) => t.start_async().await,
52            #[cfg(any(target_os = "linux", target_os = "macos"))]
53            TransportHandle::Ethernet(t) => t.start_async().await,
54            TransportHandle::Tcp(t) => t.start_async().await,
55            TransportHandle::Tor(t) => t.start_async().await,
56            #[cfg(feature = "webrtc-transport")]
57            TransportHandle::WebRtc(t) => t.start_async().await,
58            #[cfg(target_os = "linux")]
59            TransportHandle::Ble(t) => t.start_async().await,
60        }
61    }
62
63    /// Stop the transport asynchronously.
64    pub async fn stop(&mut self) -> Result<(), TransportError> {
65        match self {
66            TransportHandle::Udp(t) => t.stop_async().await,
67            #[cfg(feature = "sim-transport")]
68            TransportHandle::Sim(t) => t.stop_async().await,
69            #[cfg(any(target_os = "linux", target_os = "macos"))]
70            TransportHandle::Ethernet(t) => t.stop_async().await,
71            TransportHandle::Tcp(t) => t.stop_async().await,
72            TransportHandle::Tor(t) => t.stop_async().await,
73            #[cfg(feature = "webrtc-transport")]
74            TransportHandle::WebRtc(t) => t.stop_async().await,
75            #[cfg(target_os = "linux")]
76            TransportHandle::Ble(t) => t.stop_async().await,
77        }
78    }
79
80    /// Send data to a remote address asynchronously.
81    pub async fn send(&self, addr: &TransportAddr, data: &[u8]) -> Result<usize, TransportError> {
82        match self {
83            TransportHandle::Udp(t) => t.send_async(addr, data).await,
84            #[cfg(feature = "sim-transport")]
85            TransportHandle::Sim(t) => t.send_async(addr, data).await,
86            #[cfg(any(target_os = "linux", target_os = "macos"))]
87            TransportHandle::Ethernet(t) => t.send_async(addr, data).await,
88            TransportHandle::Tcp(t) => t.send_async(addr, data).await,
89            TransportHandle::Tor(t) => t.send_async(addr, data).await,
90            #[cfg(feature = "webrtc-transport")]
91            TransportHandle::WebRtc(t) => t.send_async(addr, data).await,
92            #[cfg(target_os = "linux")]
93            TransportHandle::Ble(t) => t.send_async(addr, data).await,
94        }
95    }
96
97    /// Get the transport ID.
98    pub fn transport_id(&self) -> TransportId {
99        match self {
100            TransportHandle::Udp(t) => t.transport_id(),
101            #[cfg(feature = "sim-transport")]
102            TransportHandle::Sim(t) => t.transport_id(),
103            #[cfg(any(target_os = "linux", target_os = "macos"))]
104            TransportHandle::Ethernet(t) => t.transport_id(),
105            TransportHandle::Tcp(t) => t.transport_id(),
106            TransportHandle::Tor(t) => t.transport_id(),
107            #[cfg(feature = "webrtc-transport")]
108            TransportHandle::WebRtc(t) => t.transport_id(),
109            #[cfg(target_os = "linux")]
110            TransportHandle::Ble(t) => t.transport_id(),
111        }
112    }
113
114    /// Get the instance name (if configured as a named instance).
115    pub fn name(&self) -> Option<&str> {
116        match self {
117            TransportHandle::Udp(t) => t.name(),
118            #[cfg(feature = "sim-transport")]
119            TransportHandle::Sim(t) => t.name(),
120            #[cfg(any(target_os = "linux", target_os = "macos"))]
121            TransportHandle::Ethernet(t) => t.name(),
122            TransportHandle::Tcp(t) => t.name(),
123            TransportHandle::Tor(t) => t.name(),
124            #[cfg(feature = "webrtc-transport")]
125            TransportHandle::WebRtc(t) => t.name(),
126            #[cfg(target_os = "linux")]
127            TransportHandle::Ble(t) => t.name(),
128        }
129    }
130
131    /// Get the transport type metadata.
132    pub fn transport_type(&self) -> &TransportType {
133        match self {
134            TransportHandle::Udp(t) => t.transport_type(),
135            #[cfg(feature = "sim-transport")]
136            TransportHandle::Sim(t) => t.transport_type(),
137            #[cfg(any(target_os = "linux", target_os = "macos"))]
138            TransportHandle::Ethernet(t) => t.transport_type(),
139            TransportHandle::Tcp(t) => t.transport_type(),
140            TransportHandle::Tor(t) => t.transport_type(),
141            #[cfg(feature = "webrtc-transport")]
142            TransportHandle::WebRtc(t) => t.transport_type(),
143            #[cfg(target_os = "linux")]
144            TransportHandle::Ble(t) => t.transport_type(),
145        }
146    }
147
148    /// Get current transport state.
149    pub fn state(&self) -> TransportState {
150        match self {
151            TransportHandle::Udp(t) => t.state(),
152            #[cfg(feature = "sim-transport")]
153            TransportHandle::Sim(t) => t.state(),
154            #[cfg(any(target_os = "linux", target_os = "macos"))]
155            TransportHandle::Ethernet(t) => t.state(),
156            TransportHandle::Tcp(t) => t.state(),
157            TransportHandle::Tor(t) => t.state(),
158            #[cfg(feature = "webrtc-transport")]
159            TransportHandle::WebRtc(t) => t.state(),
160            #[cfg(target_os = "linux")]
161            TransportHandle::Ble(t) => t.state(),
162        }
163    }
164
165    /// Get the transport MTU.
166    pub fn mtu(&self) -> u16 {
167        match self {
168            TransportHandle::Udp(t) => t.mtu(),
169            #[cfg(feature = "sim-transport")]
170            TransportHandle::Sim(t) => t.mtu(),
171            #[cfg(any(target_os = "linux", target_os = "macos"))]
172            TransportHandle::Ethernet(t) => t.mtu(),
173            TransportHandle::Tcp(t) => t.mtu(),
174            TransportHandle::Tor(t) => t.mtu(),
175            #[cfg(feature = "webrtc-transport")]
176            TransportHandle::WebRtc(t) => t.mtu(),
177            #[cfg(target_os = "linux")]
178            TransportHandle::Ble(t) => t.mtu(),
179        }
180    }
181
182    /// Get the MTU for a specific link address.
183    ///
184    /// Falls back to transport-wide MTU if the transport doesn't
185    /// support per-link MTU or the address is unknown.
186    pub fn link_mtu(&self, addr: &TransportAddr) -> u16 {
187        match self {
188            TransportHandle::Udp(t) => t.link_mtu(addr),
189            #[cfg(feature = "sim-transport")]
190            TransportHandle::Sim(t) => t.link_mtu(addr),
191            #[cfg(any(target_os = "linux", target_os = "macos"))]
192            TransportHandle::Ethernet(t) => t.link_mtu(addr),
193            TransportHandle::Tcp(t) => t.link_mtu(addr),
194            TransportHandle::Tor(t) => t.link_mtu(addr),
195            #[cfg(feature = "webrtc-transport")]
196            TransportHandle::WebRtc(t) => t.link_mtu(addr),
197            #[cfg(target_os = "linux")]
198            TransportHandle::Ble(t) => t.link_mtu(addr),
199        }
200    }
201
202    /// Get the local bound address (UDP/TCP only, returns None for other transports).
203    pub fn local_addr(&self) -> Option<std::net::SocketAddr> {
204        match self {
205            TransportHandle::Udp(t) => t.local_addr(),
206            #[cfg(feature = "sim-transport")]
207            TransportHandle::Sim(_) => None,
208            #[cfg(any(target_os = "linux", target_os = "macos"))]
209            TransportHandle::Ethernet(_) => None,
210            TransportHandle::Tcp(t) => t.local_addr(),
211            TransportHandle::Tor(_) => None,
212            #[cfg(feature = "webrtc-transport")]
213            TransportHandle::WebRtc(_) => None,
214            #[cfg(target_os = "linux")]
215            TransportHandle::Ble(_) => None,
216        }
217    }
218
219    /// Get the interface name (Ethernet only, returns None for other transports).
220    pub fn interface_name(&self) -> Option<&str> {
221        match self {
222            TransportHandle::Udp(_) => None,
223            #[cfg(feature = "sim-transport")]
224            TransportHandle::Sim(_) => None,
225            #[cfg(any(target_os = "linux", target_os = "macos"))]
226            TransportHandle::Ethernet(t) => Some(t.interface_name()),
227            TransportHandle::Tcp(_) => None,
228            TransportHandle::Tor(_) => None,
229            #[cfg(feature = "webrtc-transport")]
230            TransportHandle::WebRtc(_) => None,
231            #[cfg(target_os = "linux")]
232            TransportHandle::Ble(_) => None,
233        }
234    }
235
236    /// Get the onion service address (Tor only, returns None for other transports).
237    pub fn onion_address(&self) -> Option<&str> {
238        match self {
239            TransportHandle::Tor(t) => t.onion_address(),
240            _ => None,
241        }
242    }
243
244    /// Get cached Tor daemon monitoring info (Tor only).
245    pub fn tor_monitoring(&self) -> Option<TorMonitoringInfo> {
246        match self {
247            TransportHandle::Tor(t) => t.cached_monitoring(),
248            _ => None,
249        }
250    }
251
252    /// Get the Tor transport mode (Tor only).
253    pub fn tor_mode(&self) -> Option<&str> {
254        match self {
255            TransportHandle::Tor(t) => Some(t.mode()),
256            _ => None,
257        }
258    }
259
260    /// Drain discovered peers from this transport.
261    pub fn discover(&self) -> Result<Vec<DiscoveredPeer>, TransportError> {
262        match self {
263            TransportHandle::Udp(t) => t.discover(),
264            #[cfg(feature = "sim-transport")]
265            TransportHandle::Sim(t) => t.discover(),
266            #[cfg(any(target_os = "linux", target_os = "macos"))]
267            TransportHandle::Ethernet(t) => t.discover(),
268            TransportHandle::Tcp(t) => t.discover(),
269            TransportHandle::Tor(t) => t.discover(),
270            #[cfg(feature = "webrtc-transport")]
271            TransportHandle::WebRtc(t) => t.discover(),
272            #[cfg(target_os = "linux")]
273            TransportHandle::Ble(t) => t.discover(),
274        }
275    }
276
277    /// Whether this transport auto-connects to discovered peers.
278    pub fn auto_connect(&self) -> bool {
279        match self {
280            TransportHandle::Udp(t) => t.auto_connect(),
281            #[cfg(feature = "sim-transport")]
282            TransportHandle::Sim(t) => t.auto_connect(),
283            #[cfg(any(target_os = "linux", target_os = "macos"))]
284            TransportHandle::Ethernet(t) => t.auto_connect(),
285            TransportHandle::Tcp(t) => t.auto_connect(),
286            TransportHandle::Tor(t) => t.auto_connect(),
287            #[cfg(feature = "webrtc-transport")]
288            TransportHandle::WebRtc(t) => t.auto_connect(),
289            #[cfg(target_os = "linux")]
290            TransportHandle::Ble(t) => t.auto_connect(),
291        }
292    }
293
294    /// Whether this transport accepts inbound connections.
295    pub fn accept_connections(&self) -> bool {
296        match self {
297            TransportHandle::Udp(t) => t.accept_connections(),
298            #[cfg(feature = "sim-transport")]
299            TransportHandle::Sim(t) => t.accept_connections(),
300            #[cfg(any(target_os = "linux", target_os = "macos"))]
301            TransportHandle::Ethernet(t) => t.accept_connections(),
302            TransportHandle::Tcp(t) => t.accept_connections(),
303            TransportHandle::Tor(t) => t.accept_connections(),
304            #[cfg(feature = "webrtc-transport")]
305            TransportHandle::WebRtc(t) => t.accept_connections(),
306            #[cfg(target_os = "linux")]
307            TransportHandle::Ble(t) => t.accept_connections(),
308        }
309    }
310
311    /// Initiate a non-blocking connection to a remote address.
312    ///
313    /// For connection-oriented transports (TCP, Tor), spawns a background
314    /// task to establish the connection. For connectionless transports
315    /// (UDP, Ethernet), this is a no-op that returns Ok immediately.
316    ///
317    /// Poll `connection_state()` to check when the connection is ready.
318    pub async fn connect(&self, addr: &TransportAddr) -> Result<(), TransportError> {
319        match self {
320            TransportHandle::Udp(_) => Ok(()), // connectionless
321            #[cfg(feature = "sim-transport")]
322            TransportHandle::Sim(_) => Ok(()), // connectionless
323            #[cfg(any(target_os = "linux", target_os = "macos"))]
324            TransportHandle::Ethernet(_) => Ok(()), // connectionless
325            TransportHandle::Tcp(t) => t.connect_async(addr).await,
326            TransportHandle::Tor(t) => t.connect_async(addr).await,
327            #[cfg(feature = "webrtc-transport")]
328            TransportHandle::WebRtc(t) => t.connect_async(addr).await,
329            #[cfg(target_os = "linux")]
330            TransportHandle::Ble(t) => t.connect_async(addr).await,
331        }
332    }
333
334    /// Query the state of a connection attempt to a remote address.
335    ///
336    /// For connectionless transports, always returns `ConnectionState::Connected`
337    /// (they are always "connected"). For connection-oriented transports, returns
338    /// the current state of the background connection attempt.
339    pub fn connection_state(&self, addr: &TransportAddr) -> ConnectionState {
340        match self {
341            TransportHandle::Udp(_) => ConnectionState::Connected,
342            #[cfg(feature = "sim-transport")]
343            TransportHandle::Sim(_) => ConnectionState::Connected,
344            #[cfg(any(target_os = "linux", target_os = "macos"))]
345            TransportHandle::Ethernet(_) => ConnectionState::Connected,
346            TransportHandle::Tcp(t) => t.connection_state_sync(addr),
347            TransportHandle::Tor(t) => t.connection_state_sync(addr),
348            #[cfg(feature = "webrtc-transport")]
349            TransportHandle::WebRtc(t) => t.connection_state_sync(addr),
350            #[cfg(target_os = "linux")]
351            TransportHandle::Ble(t) => t.connection_state_sync(addr),
352        }
353    }
354
355    /// Close a specific connection on this transport.
356    ///
357    /// No-op for connectionless transports. For TCP/Tor, removes the
358    /// connection from the pool and drops the stream.
359    pub async fn close_connection(&self, addr: &TransportAddr) {
360        match self {
361            TransportHandle::Udp(t) => t.close_connection(addr),
362            #[cfg(feature = "sim-transport")]
363            TransportHandle::Sim(t) => t.close_connection(addr),
364            #[cfg(any(target_os = "linux", target_os = "macos"))]
365            TransportHandle::Ethernet(t) => t.close_connection(addr),
366            TransportHandle::Tcp(t) => t.close_connection_async(addr).await,
367            TransportHandle::Tor(t) => t.close_connection_async(addr).await,
368            #[cfg(feature = "webrtc-transport")]
369            TransportHandle::WebRtc(t) => t.close_connection_async(addr).await,
370            #[cfg(target_os = "linux")]
371            TransportHandle::Ble(t) => t.close_connection_async(addr).await,
372        }
373    }
374
375    /// Check if transport is operational.
376    pub fn is_operational(&self) -> bool {
377        self.state().is_operational()
378    }
379
380    /// Query transport-local congestion indicators.
381    ///
382    /// Returns a snapshot of congestion signals that the transport can
383    /// observe locally (e.g., kernel receive buffer drops). Fields are
384    /// `None` when the transport doesn't support that signal.
385    pub fn congestion(&self) -> TransportCongestion {
386        match self {
387            TransportHandle::Udp(t) => t.congestion(),
388            #[cfg(feature = "sim-transport")]
389            TransportHandle::Sim(_) => TransportCongestion::default(),
390            #[cfg(any(target_os = "linux", target_os = "macos"))]
391            TransportHandle::Ethernet(_) => TransportCongestion::default(),
392            TransportHandle::Tcp(_) => TransportCongestion::default(),
393            TransportHandle::Tor(_) => TransportCongestion::default(),
394            #[cfg(feature = "webrtc-transport")]
395            TransportHandle::WebRtc(_) => TransportCongestion::default(),
396            #[cfg(target_os = "linux")]
397            TransportHandle::Ble(_) => TransportCongestion::default(),
398        }
399    }
400
401    /// Get transport-specific stats as a JSON value.
402    ///
403    /// Returns a snapshot of counters for the specific transport type.
404    pub fn transport_stats(&self) -> serde_json::Value {
405        match self {
406            TransportHandle::Udp(t) => {
407                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
408            }
409            #[cfg(feature = "sim-transport")]
410            TransportHandle::Sim(t) => serde_json::to_value(t.stats()).unwrap_or_default(),
411            #[cfg(any(target_os = "linux", target_os = "macos"))]
412            TransportHandle::Ethernet(t) => {
413                let snap = t.stats().snapshot();
414                serde_json::json!({
415                    "frames_sent": snap.frames_sent,
416                    "frames_recv": snap.frames_recv,
417                    "bytes_sent": snap.bytes_sent,
418                    "bytes_recv": snap.bytes_recv,
419                    "send_errors": snap.send_errors,
420                    "recv_errors": snap.recv_errors,
421                    "beacons_sent": snap.beacons_sent,
422                    "beacons_recv": snap.beacons_recv,
423                    "frames_too_short": snap.frames_too_short,
424                    "frames_too_long": snap.frames_too_long,
425                })
426            }
427            TransportHandle::Tcp(t) => {
428                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
429            }
430            TransportHandle::Tor(t) => {
431                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
432            }
433            #[cfg(feature = "webrtc-transport")]
434            TransportHandle::WebRtc(t) => serde_json::json!({
435                "mtu": t.mtu(),
436                "state": t.state().to_string(),
437            }),
438            #[cfg(target_os = "linux")]
439            TransportHandle::Ble(t) => {
440                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
441            }
442        }
443    }
444}