use std::{collections::VecDeque, fmt};
use crate::{InstrumentError, InstrumentInterface};
#[derive(Debug, Default)]
struct IncrIndex {
index: usize,
}
impl IncrIndex {
fn next(&mut self) -> usize {
let current = self.index;
self.index += 1;
current
}
}
pub struct LoopbackInterface<T>
where
T: AsRef<[u8]> + fmt::Display + PartialEq,
{
from_host: Vec<T>,
from_inst: Vec<T>,
terminator_exp: String,
from_host_index: IncrIndex,
from_inst_index: IncrIndex,
curr_bytes: VecDeque<u8>,
terminator: String,
}
impl<T> LoopbackInterface<T>
where
T: AsRef<[u8]> + fmt::Display + PartialEq,
{
pub fn new(from_host: Vec<T>, from_inst: Vec<T>, terminator_exp: &str) -> Self {
LoopbackInterface {
from_host,
from_inst,
terminator_exp: terminator_exp.to_string(), from_host_index: IncrIndex::default(),
from_inst_index: IncrIndex::default(),
curr_bytes: VecDeque::new(),
terminator: "\n".to_string(), }
}
pub fn finalize(&mut self) {
let from_host_leftover = self.from_host.get(self.from_host_index.next());
let from_inst_leftover = self.from_inst.get(self.from_inst_index.next());
if let Some(fil) = from_host_leftover {
panic!("Leftover expected commands found from host to instrument: {fil}");
}
if let Some(fil) = from_inst_leftover {
panic!("Leftover expected commands found from instrument to host: {fil}");
}
}
fn get_next_from_host(&mut self) -> &T {
self.from_host
.get(self.from_host_index.next())
.expect("No more commands were expected from host to instrument.")
}
fn get_next_from_inst(&mut self) -> &T {
self.from_inst
.get(self.from_inst_index.next())
.expect("No more commands were expected from instrument to host.")
}
fn get_next_from_host_with_terminator(&mut self) -> String {
let cmd = self.get_next_from_host().to_string();
format!("{cmd}{}", self.terminator_exp)
}
fn get_next_from_inst_with_terminator(&mut self) -> String {
let cmd = self.get_next_from_inst().to_string();
format!("{cmd}{}", self.terminator_exp)
}
fn read_one_byte(&mut self) -> u8 {
match self.curr_bytes.pop_front() {
Some(byte) => byte,
None => {
let next_cmd = self.get_next_from_inst_with_terminator();
self.curr_bytes = next_cmd.as_bytes().iter().copied().collect();
self.read_one_byte()
}
}
}
}
impl<T> InstrumentInterface for LoopbackInterface<T>
where
T: AsRef<[u8]> + fmt::Display + PartialEq,
{
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), InstrumentError> {
for byte in buf.iter_mut() {
*byte = self.read_one_byte();
}
Ok(())
}
fn get_terminator(&self) -> &str {
self.terminator.as_str()
}
fn set_terminator(&mut self, terminator: &str) {
self.terminator = terminator.to_string();
}
fn write_raw(&mut self, cmd: &[u8]) -> Result<(), InstrumentError> {
let exp = self.get_next_from_host_with_terminator();
assert_eq!(
exp.as_bytes(),
cmd,
"Expected sendcmd '{0}', got '{1:?}'",
exp,
str::from_utf8(cmd)
);
Ok(())
}
}
impl<T: AsRef<[u8]> + fmt::Display + PartialEq> Drop for LoopbackInterface<T> {
fn drop(&mut self) {
self.finalize();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incrementing_index() {
let mut idx = IncrIndex::default();
assert_eq!(0, idx.next());
assert_eq!(1, idx.next());
assert_eq!(2, idx.next());
}
}