use crate::state::{ConnectedPort, PortState, SerialportInfo};
use serialport::{self, SerialPort};
use std::io::{self, Read, Write};
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[allow(dead_code)]
pub trait SerialPortTrait: Read + Write + Send {
fn name(&self) -> Option<String>;
fn baud_rate(&self) -> io::Result<u32>;
fn data_bits(&self) -> io::Result<serialport::DataBits>;
fn flow_control(&self) -> io::Result<serialport::FlowControl>;
fn parity(&self) -> io::Result<serialport::Parity>;
fn stop_bits(&self) -> io::Result<serialport::StopBits>;
fn timeout(&self) -> io::Result<std::time::Duration>;
fn set_baud_rate(&mut self, baud_rate: u32) -> io::Result<()>;
fn set_data_bits(&mut self, data_bits: serialport::DataBits) -> io::Result<()>;
fn set_flow_control(&mut self, flow_control: serialport::FlowControl) -> io::Result<()>;
fn set_parity(&mut self, parity: serialport::Parity) -> io::Result<()>;
fn set_stop_bits(&mut self, stop_bits: serialport::StopBits) -> io::Result<()>;
fn set_timeout(&mut self, timeout: std::time::Duration) -> io::Result<()>;
fn write_request_to_send(&mut self, level: bool) -> io::Result<()>;
fn write_data_terminal_ready(&mut self, level: bool) -> io::Result<()>;
fn read_clear_to_send(&mut self) -> io::Result<bool>;
fn read_data_set_ready(&mut self) -> io::Result<bool>;
fn read_ring_indicator(&mut self) -> io::Result<bool>;
fn read_carrier_detect(&mut self) -> io::Result<bool>;
fn bytes_to_read(&self) -> io::Result<u32>;
fn bytes_to_write(&self) -> io::Result<u32>;
fn clear(&self, buffer_to_clear: serialport::ClearBuffer) -> io::Result<()>;
fn try_clone(&self) -> io::Result<Box<dyn SerialPort>>;
fn set_break(&self) -> io::Result<()>;
fn clear_break(&self) -> io::Result<()>;
}
#[allow(dead_code)]
pub struct MockSerialPort {
pub rx_buffer: Vec<u8>,
pub rx_script: Vec<Vec<u8>>,
pub captured_tx: Arc<Mutex<Vec<u8>>>,
pub next_read_error: Option<io::ErrorKind>,
pub baud_rate: u32,
pub data_bits: serialport::DataBits,
pub flow_control: serialport::FlowControl,
pub parity: serialport::Parity,
pub stop_bits: serialport::StopBits,
pub timeout: Duration,
}
#[allow(dead_code)]
impl MockSerialPort {
pub fn new() -> Self {
Self::default()
}
}
impl Default for MockSerialPort {
fn default() -> Self {
Self {
rx_buffer: Vec::new(),
rx_script: Vec::new(),
captured_tx: Arc::new(Mutex::new(Vec::new())),
next_read_error: None,
baud_rate: 9600,
data_bits: serialport::DataBits::Eight,
flow_control: serialport::FlowControl::None,
parity: serialport::Parity::None,
stop_bits: serialport::StopBits::One,
timeout: Duration::from_millis(1000),
}
}
}
#[allow(dead_code)]
impl MockSerialPort {
pub fn with_rx_script(chunks: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Self {
let mut mock = Self::new();
mock.rx_script = chunks.into_iter().map(|c| c.as_ref().to_vec()).collect();
mock
}
pub fn push_rx(&mut self, chunk: impl AsRef<[u8]>) {
self.rx_buffer.extend_from_slice(chunk.as_ref());
}
pub fn inject_read_error(&mut self, kind: io::ErrorKind) {
self.next_read_error = Some(kind);
}
pub fn captured_tx(&self) -> Vec<u8> {
self.captured_tx.lock().unwrap().clone()
}
fn pull_rx(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.rx_buffer.is_empty() {
if let Some(chunk) = self.rx_script.first().cloned() {
self.rx_script.remove(0);
self.rx_buffer = chunk;
}
}
let len = std::cmp::min(buf.len(), self.rx_buffer.len());
if len > 0 {
buf[..len].copy_from_slice(&self.rx_buffer[..len]);
self.rx_buffer.drain(..len);
}
Ok(len)
}
}
impl SerialPort for MockSerialPort {
fn name(&self) -> Option<String> {
Some("COM1".to_string())
}
fn baud_rate(&self) -> Result<u32, serialport::Error> {
Ok(self.baud_rate)
}
fn data_bits(&self) -> Result<serialport::DataBits, serialport::Error> {
Ok(self.data_bits)
}
fn flow_control(&self) -> Result<serialport::FlowControl, serialport::Error> {
Ok(self.flow_control)
}
fn parity(&self) -> Result<serialport::Parity, serialport::Error> {
Ok(self.parity)
}
fn stop_bits(&self) -> Result<serialport::StopBits, serialport::Error> {
Ok(self.stop_bits)
}
fn timeout(&self) -> Duration {
self.timeout
}
fn set_baud_rate(&mut self, rate: u32) -> Result<(), serialport::Error> {
self.baud_rate = rate;
Ok(())
}
fn set_data_bits(&mut self, bits: serialport::DataBits) -> Result<(), serialport::Error> {
self.data_bits = bits;
Ok(())
}
fn set_flow_control(&mut self, flow: serialport::FlowControl) -> Result<(), serialport::Error> {
self.flow_control = flow;
Ok(())
}
fn set_parity(&mut self, parity: serialport::Parity) -> Result<(), serialport::Error> {
self.parity = parity;
Ok(())
}
fn set_stop_bits(&mut self, bits: serialport::StopBits) -> Result<(), serialport::Error> {
self.stop_bits = bits;
Ok(())
}
fn set_timeout(&mut self, timeout: Duration) -> Result<(), serialport::Error> {
self.timeout = timeout;
Ok(())
}
fn write_request_to_send(&mut self, _level: bool) -> Result<(), serialport::Error> {
Ok(())
}
fn write_data_terminal_ready(&mut self, _level: bool) -> Result<(), serialport::Error> {
Ok(())
}
fn read_clear_to_send(&mut self) -> Result<bool, serialport::Error> {
Ok(true)
}
fn read_data_set_ready(&mut self) -> Result<bool, serialport::Error> {
Ok(true)
}
fn read_ring_indicator(&mut self) -> Result<bool, serialport::Error> {
Ok(true)
}
fn read_carrier_detect(&mut self) -> Result<bool, serialport::Error> {
Ok(true)
}
fn bytes_to_read(&self) -> Result<u32, serialport::Error> {
Ok((self.rx_buffer.len() + self.rx_script.iter().map(|c| c.len()).sum::<usize>()) as u32)
}
fn bytes_to_write(&self) -> Result<u32, serialport::Error> {
Ok(0)
}
fn clear(&self, _buffer_to_clear: serialport::ClearBuffer) -> Result<(), serialport::Error> {
Ok(())
}
fn try_clone(&self) -> Result<Box<dyn SerialPort>, serialport::Error> {
Ok(Box::new(MockSerialPort {
rx_buffer: self.rx_buffer.clone(),
rx_script: self.rx_script.clone(),
captured_tx: self.captured_tx.clone(),
next_read_error: self.next_read_error,
baud_rate: self.baud_rate,
data_bits: self.data_bits,
flow_control: self.flow_control,
parity: self.parity,
stop_bits: self.stop_bits,
timeout: self.timeout,
}))
}
fn set_break(&self) -> Result<(), serialport::Error> {
Ok(())
}
fn clear_break(&self) -> Result<(), serialport::Error> {
Ok(())
}
}
impl Read for MockSerialPort {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if let Some(kind) = self.next_read_error.take() {
return Err(io::Error::new(kind, "injected read error"));
}
self.pull_rx(buf)
}
}
impl Write for MockSerialPort {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.captured_tx.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[allow(dead_code)]
pub fn create_mock_serialport_info() -> SerialportInfo {
SerialportInfo {
state: PortState::Connected(ConnectedPort::new(Box::new(MockSerialPort::new()))),
}
}