ant_quic/constrained/
transport.rs1use super::adapter::{AdapterEvent, ConstrainedEngineAdapter, EngineOutput};
15use super::engine::EngineConfig;
16use super::types::{ConnectionId, ConstrainedError};
17use crate::transport::{TransportAddr, TransportCapabilities};
18use std::sync::{Arc, Mutex};
19use tokio::sync::mpsc;
20
21#[derive(Debug, Clone)]
23pub struct ConstrainedTransportConfig {
24 pub engine_config: EngineConfig,
26 pub outbound_buffer_size: usize,
28 pub event_buffer_size: usize,
30}
31
32impl Default for ConstrainedTransportConfig {
33 fn default() -> Self {
34 Self {
35 engine_config: EngineConfig::default(),
36 outbound_buffer_size: 64,
37 event_buffer_size: 32,
38 }
39 }
40}
41
42impl ConstrainedTransportConfig {
43 pub fn for_ble() -> Self {
45 Self {
46 engine_config: EngineConfig::for_ble(),
47 outbound_buffer_size: 32,
48 event_buffer_size: 16,
49 }
50 }
51
52 pub fn for_lora() -> Self {
54 Self {
55 engine_config: EngineConfig::for_lora(),
56 outbound_buffer_size: 8,
57 event_buffer_size: 8,
58 }
59 }
60}
61
62#[derive(Clone, Debug)]
64pub struct ConstrainedHandle {
65 adapter: Arc<Mutex<ConstrainedEngineAdapter>>,
67 outbound_tx: mpsc::Sender<EngineOutput>,
69}
70
71impl ConstrainedHandle {
72 pub fn connect(&self, remote: &TransportAddr) -> Result<ConnectionId, ConstrainedError> {
74 let mut adapter = self
75 .adapter
76 .lock()
77 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
78
79 let (conn_id, outputs) = adapter.connect(remote)?;
80
81 for output in outputs {
83 let _ = self.outbound_tx.try_send(output);
84 }
85
86 Ok(conn_id)
87 }
88
89 pub fn send(&self, connection_id: ConnectionId, data: &[u8]) -> Result<(), ConstrainedError> {
91 let mut adapter = self
92 .adapter
93 .lock()
94 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
95
96 let outputs = adapter.send(connection_id, data)?;
97
98 for output in outputs {
99 let _ = self.outbound_tx.try_send(output);
100 }
101
102 Ok(())
103 }
104
105 pub fn recv(&self, connection_id: ConnectionId) -> Result<Option<Vec<u8>>, ConstrainedError> {
107 let mut adapter = self
108 .adapter
109 .lock()
110 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
111
112 Ok(adapter.recv(connection_id))
113 }
114
115 pub fn close(&self, connection_id: ConnectionId) -> Result<(), ConstrainedError> {
117 let mut adapter = self
118 .adapter
119 .lock()
120 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
121
122 let outputs = adapter.close(connection_id)?;
123
124 for output in outputs {
125 let _ = self.outbound_tx.try_send(output);
126 }
127
128 Ok(())
129 }
130
131 pub fn connection_count(&self) -> usize {
133 self.adapter
134 .lock()
135 .map(|a| a.connection_count())
136 .unwrap_or(0)
137 }
138
139 pub fn process_incoming(
141 &self,
142 source: &TransportAddr,
143 data: &[u8],
144 ) -> Result<(), ConstrainedError> {
145 let mut adapter = self
146 .adapter
147 .lock()
148 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
149
150 let outputs = adapter.process_incoming(source, data)?;
151
152 for output in outputs {
153 let _ = self.outbound_tx.try_send(output);
154 }
155
156 Ok(())
157 }
158
159 pub fn poll(&self) -> Vec<EngineOutput> {
161 let mut adapter = match self.adapter.lock() {
162 Ok(a) => a,
163 Err(_) => return Vec::new(),
164 };
165
166 adapter.poll()
167 }
168
169 pub fn next_event(&self) -> Option<AdapterEvent> {
171 self.adapter.lock().ok().and_then(|mut a| a.next_event())
172 }
173
174 pub fn connection_state(
176 &self,
177 connection_id: ConnectionId,
178 ) -> Option<crate::constrained::ConnectionState> {
179 self.adapter
180 .lock()
181 .ok()
182 .and_then(|a| a.connection_state(connection_id))
183 }
184
185 pub fn active_connections(&self) -> Vec<ConnectionId> {
187 self.adapter
188 .lock()
189 .ok()
190 .map(|a| a.active_connections())
191 .unwrap_or_default()
192 }
193}
194
195pub struct ConstrainedTransport {
200 adapter: Arc<Mutex<ConstrainedEngineAdapter>>,
202 outbound_tx: mpsc::Sender<EngineOutput>,
204 outbound_rx: mpsc::Receiver<EngineOutput>,
206 config: ConstrainedTransportConfig,
208}
209
210impl ConstrainedTransport {
211 pub fn new(config: ConstrainedTransportConfig) -> Self {
213 let (outbound_tx, outbound_rx) = mpsc::channel(config.outbound_buffer_size);
214 let adapter = ConstrainedEngineAdapter::new(config.engine_config.clone());
215
216 Self {
217 adapter: Arc::new(Mutex::new(adapter)),
218 outbound_tx,
219 outbound_rx,
220 config,
221 }
222 }
223
224 pub fn for_ble() -> Self {
226 Self::new(ConstrainedTransportConfig::for_ble())
227 }
228
229 pub fn for_lora() -> Self {
231 Self::new(ConstrainedTransportConfig::for_lora())
232 }
233
234 pub fn handle(&self) -> ConstrainedHandle {
236 ConstrainedHandle {
237 adapter: Arc::clone(&self.adapter),
238 outbound_tx: self.outbound_tx.clone(),
239 }
240 }
241
242 pub fn take_outbound_rx(&mut self) -> mpsc::Receiver<EngineOutput> {
246 let (new_tx, new_rx) = mpsc::channel(self.config.outbound_buffer_size);
247
248 let _ = std::mem::replace(&mut self.outbound_tx, new_tx);
250 std::mem::replace(&mut self.outbound_rx, new_rx)
251 }
252
253 pub fn process_incoming(
255 &self,
256 source: &TransportAddr,
257 data: &[u8],
258 ) -> Result<(), ConstrainedError> {
259 let mut adapter = self
260 .adapter
261 .lock()
262 .map_err(|_| ConstrainedError::Transport("adapter lock poisoned".into()))?;
263
264 let outputs = adapter.process_incoming(source, data)?;
265
266 for output in outputs {
267 let _ = self.outbound_tx.try_send(output);
268 }
269
270 Ok(())
271 }
272
273 pub fn poll(&self) {
275 if let Ok(mut adapter) = self.adapter.lock() {
276 let outputs = adapter.poll();
277 for output in outputs {
278 let _ = self.outbound_tx.try_send(output);
279 }
280 }
281 }
282
283 pub fn should_use_constrained(capabilities: &TransportCapabilities) -> bool {
285 !capabilities.supports_full_quic()
286 }
287}
288
289impl std::fmt::Debug for ConstrainedTransport {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 f.debug_struct("ConstrainedTransport")
292 .field("config", &self.config)
293 .field(
294 "connection_count",
295 &self
296 .adapter
297 .lock()
298 .map(|a| a.connection_count())
299 .unwrap_or(0),
300 )
301 .finish()
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_constrained_transport_creation() {
311 let transport = ConstrainedTransport::for_ble();
312 let handle = transport.handle();
313 assert_eq!(handle.connection_count(), 0);
314 }
315
316 #[test]
317 fn test_constrained_handle_connect() {
318 let transport = ConstrainedTransport::for_ble();
319 let handle = transport.handle();
320
321 let addr = TransportAddr::Ble {
322 device_id: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
323 service_uuid: None,
324 };
325
326 let result = handle.connect(&addr);
327 assert!(result.is_ok());
328 assert_eq!(handle.connection_count(), 1);
329 }
330
331 #[test]
332 fn test_constrained_config_presets() {
333 let ble_config = ConstrainedTransportConfig::for_ble();
334 assert_eq!(ble_config.outbound_buffer_size, 32);
335
336 let lora_config = ConstrainedTransportConfig::for_lora();
337 assert_eq!(lora_config.outbound_buffer_size, 8);
338 }
339
340 #[test]
341 fn test_should_use_constrained() {
342 use crate::transport::TransportCapabilities;
343
344 let ble_caps = TransportCapabilities::ble();
346 assert!(ConstrainedTransport::should_use_constrained(&ble_caps));
347
348 let lora_caps = TransportCapabilities::lora_long_range();
350 assert!(ConstrainedTransport::should_use_constrained(&lora_caps));
351
352 let broadband_caps = TransportCapabilities::broadband();
354 assert!(!ConstrainedTransport::should_use_constrained(
355 &broadband_caps
356 ));
357 }
358
359 #[tokio::test]
360 async fn test_handle_clone() {
361 let transport = ConstrainedTransport::for_ble();
362 let handle1 = transport.handle();
363 let handle2 = transport.handle();
364
365 let addr = TransportAddr::Ble {
367 device_id: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66],
368 service_uuid: None,
369 };
370
371 let _ = handle1.connect(&addr);
372 assert_eq!(handle1.connection_count(), 1);
373 assert_eq!(handle2.connection_count(), 1);
374 }
375}