use std::io;
use std::time::Duration;
pub const TS_PACKET_LEN: usize = 188;
pub trait CiDataDevice {
fn write(&mut self, ts: &[u8]) -> io::Result<()>;
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
fn poll(&mut self, timeout: Duration) -> io::Result<bool>;
}
fn check_aligned(len: usize, what: &'static str) -> io::Result<()> {
if len % TS_PACKET_LEN == 0 {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, what))
}
}
#[derive(Debug, Default)]
pub struct MockCiDataDevice {
pub descrambled: std::collections::VecDeque<Vec<u8>>,
pub written: Vec<Vec<u8>>,
}
impl MockCiDataDevice {
#[must_use]
pub fn new(descrambled: impl IntoIterator<Item = Vec<u8>>) -> Self {
Self {
descrambled: descrambled.into_iter().collect(),
written: Vec::new(),
}
}
#[must_use]
pub fn written_ts(&self) -> Vec<u8> {
self.written.iter().flatten().copied().collect()
}
}
impl CiDataDevice for MockCiDataDevice {
fn write(&mut self, ts: &[u8]) -> io::Result<()> {
check_aligned(ts.len(), "write not a multiple of 188 bytes")?;
self.written.push(ts.to_vec());
Ok(())
}
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
check_aligned(buf.len(), "read buffer not a multiple of 188 bytes")?;
match self.descrambled.pop_front() {
Some(ts) => {
let n = ts.len().min(buf.len());
buf[..n].copy_from_slice(&ts[..n]);
Ok(n)
}
None => Ok(0),
}
}
fn poll(&mut self, _timeout: Duration) -> io::Result<bool> {
Ok(!self.descrambled.is_empty())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn packet(fill: u8) -> Vec<u8> {
let mut p = vec![fill; TS_PACKET_LEN];
p[0] = 0x47; p
}
#[test]
fn mock_records_writes_and_replays_descrambled() {
let mut dev = MockCiDataDevice::new([packet(0xAA), packet(0xBB)]);
dev.write(&packet(0x11)).unwrap();
dev.write(&packet(0x22)).unwrap();
let mut buf = [0u8; TS_PACKET_LEN];
assert!(dev.poll(Duration::ZERO).unwrap());
assert_eq!(dev.read(&mut buf).unwrap(), TS_PACKET_LEN);
assert_eq!(buf[1], 0xAA);
assert_eq!(dev.read(&mut buf).unwrap(), TS_PACKET_LEN);
assert_eq!(buf[1], 0xBB);
assert_eq!(dev.read(&mut buf).unwrap(), 0); assert!(!dev.poll(Duration::ZERO).unwrap());
assert_eq!(dev.written_ts().len(), 2 * TS_PACKET_LEN);
}
#[test]
fn rejects_unaligned_io() {
let mut dev = MockCiDataDevice::new([]);
assert_eq!(
dev.write(&[0x47; 100]).unwrap_err().kind(),
io::ErrorKind::InvalidInput
);
let mut buf = [0u8; 100];
assert_eq!(
dev.read(&mut buf).unwrap_err().kind(),
io::ErrorKind::InvalidInput
);
}
}