use crate::command::Command;
use crate::errors::*;
use std::io::{Read, Write};
use std::net::{TcpStream, ToSocketAddrs};
pub struct Tnc<T: Read + Write> {
pub stream: T,
port: u8,
}
impl Tnc<TcpStream> {
pub fn connect<A: ToSocketAddrs>(addr: A) -> std::io::Result<Self> {
let stream = TcpStream::connect(addr)?;
Ok(Self { stream, port: 0 })
}
}
impl<T: Read + Write> Tnc<T> {
pub fn new(stream: T) -> Self {
Self { stream, port: 0 }
}
pub fn set_port(&mut self, port: u8) {
if port > 15 {
panic!("Port number is invalid");
}
self.port = port;
}
pub fn send_frame(&mut self, data: &[u8]) -> std::io::Result<usize> {
let command = Command::borrowed_frame(data);
command.write(self.port, &mut self.stream)
}
pub fn set_tx_delay(&mut self, delay: u8) -> std::io::Result<usize> {
let command = Command::TxDelay(delay);
command.write(self.port, &mut self.stream)
}
pub fn exit(mut self) -> std::io::Result<usize> {
let command = Command::Return;
let c = command.write(self.port, &mut self.stream)?;
self.flush()?;
Ok(c)
}
pub fn flush(&mut self) -> std::io::Result<()> {
self.stream.flush()
}
pub fn read_frame(&mut self) -> Result<(u8, Vec<u8>), ReadError> {
let (port, cmd) = Command::read(&mut self.stream)?;
if let Command::DataFrame(data) = cmd {
Ok((port, data.into_owned()))
} else {
Err(ReadError::MalformedData)
}
}
}
mod tests {
use super::*;
#[derive(Default)]
struct MockStream {
read_buffer: Vec<u8>,
write_buffer: Vec<u8>,
}
impl Write for MockStream {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
self.write_buffer.extend(bytes);
Ok(bytes.len())
}
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
}
impl Read for MockStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
let len = buf.len().min(self.read_buffer.len());
buf[..len].clone_from_slice(&self.read_buffer[..len]);
self.read_buffer = self.read_buffer[len..].to_vec();
Ok(len)
}
}
#[test]
fn test_send_frame_works_corrently() {
let mut tnc = Tnc {
port: 0,
stream: MockStream::default(),
};
assert_eq!(7, tnc.send_frame("TEST".as_bytes()).unwrap());
assert_eq!(
vec![0xC0, 0x00, 0x54, 0x45, 0x53, 0x54, 0xC0],
tnc.stream.write_buffer
);
let mut tnc = Tnc {
port: 0,
stream: MockStream::default(),
};
tnc.set_port(5);
assert_eq!(8, tnc.send_frame("Hello".as_bytes()).unwrap());
assert_eq!(
vec![0xC0, 0x50, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xC0],
tnc.stream.write_buffer
);
let mut tnc = Tnc {
port: 0,
stream: MockStream::default(),
};
assert_eq!(7, tnc.send_frame(&[0xC0, 0xDB]).unwrap());
assert_eq!(
vec![0xC0, 0x00, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0],
tnc.stream.write_buffer
);
}
#[test]
fn test_set_tx_delay_works_correctly() {
let mut tnc = Tnc {
port: 14,
stream: MockStream::default(),
};
assert_eq!(4, tnc.set_tx_delay(100).unwrap());
assert_eq!(vec![0xC0, 0xE1, 100, 0xC0], tnc.stream.write_buffer);
}
#[test]
fn test_read_frame_works_correctly() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![0xC0, 0x00, 0x54, 0x45, 0x53, 0x54, 0xC0],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(0, port);
assert_eq!("TEST".as_bytes(), data);
}
#[test]
fn test_read_frame_sets_port_correctly() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![0xC0, 0x50, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xC0],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(5, port);
assert_eq!("Hello".as_bytes(), data);
}
#[test]
fn test_read_frame_multiple_frames() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![
0xC0, 0x50, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xC0, 0xC0, 0x00, 0x54, 0x45, 0x53,
0x54, 0xC0,
],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(5, port);
assert_eq!("Hello".as_bytes(), data);
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(0, port);
assert_eq!("TEST".as_bytes(), data);
}
#[test]
fn test_read_frame_escape_works_correctly() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![0xC0, 0x00, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(0, port);
assert_eq!(vec![0xC0, 0xDB], data);
}
#[test]
fn test_read_frame_ignores_additional_c0s() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0,
],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(0, port);
assert_eq!(vec![0xC0, 0xDB], data);
}
#[test]
fn test_read_frame_requires_c0() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![0x00, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0],
write_buffer: vec![],
},
};
assert!(matches!(
tnc.read_frame().unwrap_err(),
ReadError::MalformedData
));
}
#[test]
fn test_bytes_after_c0_are_ignored() {
let mut tnc = Tnc {
port: 0,
stream: MockStream {
read_buffer: vec![
0xC0, 0x00, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0, 0xA, 0xB, 0xC, 0xAB, 0xC0, 0x12, 0x53,
],
write_buffer: vec![],
},
};
let (port, data) = tnc.read_frame().unwrap();
assert_eq!(0, port);
assert_eq!(vec![0xC0, 0xDB], data);
}
}