1use std::time::Duration;
8
9use crate::U3vError;
10
11pub trait UsbTransfer: Send + Sync {
17 fn bulk_write(&self, endpoint: u8, data: &[u8], timeout: Duration) -> Result<usize, U3vError>;
19
20 fn bulk_read(&self, endpoint: u8, buf: &mut [u8], timeout: Duration)
23 -> Result<usize, U3vError>;
24}
25
26#[cfg(feature = "usb")]
31pub use self::rusb_impl::RusbTransfer;
32
33#[cfg(feature = "usb")]
34mod rusb_impl {
35 use super::*;
36 use std::sync::Arc;
37
38 pub struct RusbTransfer {
40 handle: Arc<rusb::DeviceHandle<rusb::Context>>,
41 }
42
43 impl RusbTransfer {
44 pub fn new(handle: Arc<rusb::DeviceHandle<rusb::Context>>) -> Self {
46 Self { handle }
47 }
48 }
49
50 impl UsbTransfer for RusbTransfer {
51 fn bulk_write(
52 &self,
53 endpoint: u8,
54 data: &[u8],
55 timeout: Duration,
56 ) -> Result<usize, U3vError> {
57 self.handle
58 .write_bulk(endpoint, data, timeout)
59 .map_err(|e| U3vError::Usb(e.to_string()))
60 }
61
62 fn bulk_read(
63 &self,
64 endpoint: u8,
65 buf: &mut [u8],
66 timeout: Duration,
67 ) -> Result<usize, U3vError> {
68 self.handle
69 .read_bulk(endpoint, buf, timeout)
70 .map_err(|e| U3vError::Usb(e.to_string()))
71 }
72 }
73}
74
75use std::collections::{HashMap, VecDeque};
80use std::sync::Mutex;
81
82pub struct MockUsbTransfer {
95 reads: Mutex<HashMap<u8, VecDeque<Vec<u8>>>>,
97 writes: Mutex<HashMap<u8, Vec<Vec<u8>>>>,
99}
100
101impl MockUsbTransfer {
102 pub fn new() -> Self {
104 Self {
105 reads: Mutex::new(HashMap::new()),
106 writes: Mutex::new(HashMap::new()),
107 }
108 }
109
110 pub fn enqueue_read(&self, endpoint: u8, data: Vec<u8>) {
113 self.reads
114 .lock()
115 .expect("mock read queue poisoned")
116 .entry(endpoint)
117 .or_default()
118 .push_back(data);
119 }
120
121 pub fn take_writes(&self, endpoint: u8) -> Vec<Vec<u8>> {
123 self.writes
124 .lock()
125 .expect("mock write log poisoned")
126 .remove(&endpoint)
127 .unwrap_or_default()
128 }
129}
130
131impl Default for MockUsbTransfer {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl UsbTransfer for MockUsbTransfer {
138 fn bulk_write(&self, endpoint: u8, data: &[u8], _timeout: Duration) -> Result<usize, U3vError> {
139 let len = data.len();
140 self.writes
141 .lock()
142 .expect("mock write log poisoned")
143 .entry(endpoint)
144 .or_default()
145 .push(data.to_vec());
146 Ok(len)
147 }
148
149 fn bulk_read(
150 &self,
151 endpoint: u8,
152 buf: &mut [u8],
153 _timeout: Duration,
154 ) -> Result<usize, U3vError> {
155 let mut reads = self.reads.lock().expect("mock read queue poisoned");
156 let queue = reads.get_mut(&endpoint).ok_or_else(|| {
157 U3vError::Protocol(format!("no queued read for endpoint {endpoint:#04x}"))
158 })?;
159 let data = queue.pop_front().ok_or_else(|| {
160 U3vError::Protocol(format!("read queue exhausted for endpoint {endpoint:#04x}"))
161 })?;
162 let n = data.len().min(buf.len());
163 buf[..n].copy_from_slice(&data[..n]);
164 Ok(n)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn mock_write_then_read() {
174 let mock = MockUsbTransfer::new();
175 let ep_out = 0x01;
176 let ep_in = 0x81;
177
178 let written = mock
180 .bulk_write(ep_out, &[1, 2, 3], Duration::from_millis(100))
181 .unwrap();
182 assert_eq!(written, 3);
183
184 mock.enqueue_read(ep_in, vec![4, 5, 6, 7]);
186 let mut buf = [0u8; 8];
187 let n = mock
188 .bulk_read(ep_in, &mut buf, Duration::from_millis(100))
189 .unwrap();
190 assert_eq!(n, 4);
191 assert_eq!(&buf[..4], &[4, 5, 6, 7]);
192
193 let writes = mock.take_writes(ep_out);
195 assert_eq!(writes.len(), 1);
196 assert_eq!(writes[0], &[1, 2, 3]);
197 }
198
199 #[test]
200 fn mock_read_exhausted_returns_error() {
201 let mock = MockUsbTransfer::new();
202 let mut buf = [0u8; 4];
203 let err = mock
204 .bulk_read(0x81, &mut buf, Duration::from_millis(100))
205 .unwrap_err();
206 assert!(matches!(err, U3vError::Protocol(_)));
207 }
208}