use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SlotInfo {
pub num: u8,
pub module_ready: bool,
pub module_present: bool,
}
#[derive(Debug)]
pub struct DeviceOpError {
pub op: String,
pub device: String,
source: io::Error,
}
impl DeviceOpError {
#[must_use]
pub fn wrap(op: impl Into<String>, device: &str, source: io::Error) -> io::Error {
let kind = source.kind();
io::Error::new(
kind,
DeviceOpError {
op: op.into(),
device: device.to_owned(),
source,
},
)
}
#[must_use]
pub fn errno(&self) -> Option<i32> {
self.source.raw_os_error()
}
#[must_use]
pub fn errno_of(err: &io::Error) -> Option<i32> {
err.raw_os_error().or_else(|| {
err.get_ref()
.and_then(|inner| inner.downcast_ref::<DeviceOpError>())
.and_then(DeviceOpError::errno)
})
}
}
impl std::fmt::Display for DeviceOpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {} failed: {}", self.device, self.op, self.source)
}
}
impl std::error::Error for DeviceOpError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
pub trait CaDevice {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
fn write(&mut self, buf: &[u8]) -> io::Result<()>;
fn reset(&mut self) -> io::Result<()>;
fn reset_settle(&self) -> std::time::Duration {
std::time::Duration::ZERO
}
fn slot_info(&mut self) -> io::Result<SlotInfo>;
fn poll(&mut self, timeout: std::time::Duration) -> io::Result<bool>;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DeviceOp {
Write(Vec<u8>),
Reset,
SlotInfo,
}
#[derive(Debug, Default)]
pub struct MockCaDevice {
pub inbound: std::collections::VecDeque<Vec<u8>>,
pub ops: Vec<DeviceOp>,
pub slot: SlotInfo,
}
impl MockCaDevice {
#[must_use]
pub fn new(inbound: impl IntoIterator<Item = Vec<u8>>) -> Self {
Self {
inbound: inbound.into_iter().collect(),
ops: Vec::new(),
slot: SlotInfo {
num: 0,
module_ready: true,
module_present: true,
},
}
}
#[must_use]
pub fn written(&self) -> Vec<u8> {
self.ops
.iter()
.filter_map(|o| match o {
DeviceOp::Write(b) => Some(b.clone()),
_ => None,
})
.flatten()
.collect()
}
}
impl CaDevice for MockCaDevice {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.inbound.pop_front() {
Some(frame) => {
let n = frame.len().min(buf.len());
buf[..n].copy_from_slice(&frame[..n]);
Ok(n)
}
None => Ok(0),
}
}
fn write(&mut self, buf: &[u8]) -> io::Result<()> {
self.ops.push(DeviceOp::Write(buf.to_vec()));
Ok(())
}
fn reset(&mut self) -> io::Result<()> {
self.ops.push(DeviceOp::Reset);
Ok(())
}
fn slot_info(&mut self) -> io::Result<SlotInfo> {
self.ops.push(DeviceOp::SlotInfo);
Ok(self.slot)
}
fn poll(&mut self, _timeout: std::time::Duration) -> io::Result<bool> {
Ok(!self.inbound.is_empty())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum LinkEvent {
Tx(Vec<u8>),
Rx(Vec<u8>),
Reset,
SlotInfo(SlotInfo),
}
#[derive(Debug, Default)]
pub struct RecordingCaDevice<D> {
inner: D,
pub log: Vec<LinkEvent>,
last_slot: Option<SlotInfo>,
}
impl<D: CaDevice> RecordingCaDevice<D> {
pub fn new(inner: D) -> Self {
Self {
inner,
log: Vec::new(),
last_slot: None,
}
}
#[must_use]
pub fn log(&self) -> &[LinkEvent] {
&self.log
}
pub fn inner(&self) -> &D {
&self.inner
}
}
impl<D: CaDevice> CaDevice for RecordingCaDevice<D> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.inner.read(buf)?;
if n > 0 {
self.log.push(LinkEvent::Rx(buf[..n].to_vec()));
}
Ok(n)
}
fn write(&mut self, buf: &[u8]) -> io::Result<()> {
self.log.push(LinkEvent::Tx(buf.to_vec()));
self.inner.write(buf)
}
fn reset(&mut self) -> io::Result<()> {
self.log.push(LinkEvent::Reset);
self.inner.reset()
}
fn reset_settle(&self) -> std::time::Duration {
self.inner.reset_settle()
}
fn slot_info(&mut self) -> io::Result<SlotInfo> {
let si = self.inner.slot_info()?;
if self.last_slot != Some(si) {
self.log.push(LinkEvent::SlotInfo(si));
self.last_slot = Some(si);
}
Ok(si)
}
fn poll(&mut self, timeout: std::time::Duration) -> io::Result<bool> {
self.inner.poll(timeout)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recording_device_captures_both_directions() {
let inner = MockCaDevice::new([vec![0x83, 0x01, 0x01]]);
let mut dev = RecordingCaDevice::new(inner);
dev.reset().unwrap();
dev.write(&[0x82, 0x01, 0x01]).unwrap();
let mut buf = [0u8; 16];
dev.read(&mut buf).unwrap();
assert_eq!(
dev.log(),
&[
LinkEvent::Reset,
LinkEvent::Tx(vec![0x82, 0x01, 0x01]),
LinkEvent::Rx(vec![0x83, 0x01, 0x01]),
]
);
}
#[test]
fn mock_records_writes_and_replays_inbound() {
let mut dev = MockCaDevice::new([vec![0x01, 0x02], vec![0x03]]);
dev.write(&[0xAA, 0xBB]).unwrap();
dev.reset().unwrap();
let mut buf = [0u8; 16];
assert_eq!(dev.read(&mut buf).unwrap(), 2);
assert_eq!(&buf[..2], &[0x01, 0x02]);
assert_eq!(dev.read(&mut buf).unwrap(), 1);
assert_eq!(dev.read(&mut buf).unwrap(), 0); assert_eq!(dev.written(), vec![0xAA, 0xBB]);
assert_eq!(dev.ops[1], DeviceOp::Reset);
}
}