use std::io;
use bytes::BytesMut;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpStream, ToSocketAddrs},
};
use tokio_util::codec::Decoder;
use xvc_protocol::{
BorrowedMessage, Message, XvcInfo, error::ReadError, tokio_codec::XvcInfoDecoder,
};
pub struct XvcClient {
tcp: TcpStream,
}
impl XvcClient {
pub async fn connect(addr: impl ToSocketAddrs) -> io::Result<XvcClient> {
Ok(XvcClient {
tcp: TcpStream::connect(addr).await?,
})
}
pub async fn get_info(&mut self) -> Result<XvcInfo, ReadError> {
self.write_message(Message::GetInfo).await?;
let mut buf = BytesMut::new();
loop {
match XvcInfoDecoder.decode(&mut buf)? {
Some(info) => return Ok(info),
None => {
if self.tcp.read_buf(&mut buf).await? == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"connection closed while reading server info",
)
.into());
}
}
}
}
}
pub async fn set_tck(&mut self, period_ns: u32) -> Result<u32, ReadError> {
self.write_message(Message::SetTck { period_ns }).await?;
let mut buf = [0u8; 4];
self.tcp.read_exact(&mut buf).await?;
Ok(u32::from_le_bytes(buf))
}
pub async fn shift(
&mut self,
num_bits: u32,
tms: &[u8],
tdi: &[u8],
) -> Result<Box<[u8]>, ReadError> {
let num_bytes = num_bits.div_ceil(8) as usize;
assert!(
tms.len() == num_bytes && tdi.len() == num_bytes,
"tms/tdi must be {num_bytes} bytes for {num_bits} bits, got {} / {}",
tms.len(),
tdi.len(),
);
self.write_message(BorrowedMessage::Shift { num_bits, tms, tdi })
.await?;
let mut buf = vec![0u8; num_bytes];
self.tcp.read_exact(&mut buf).await?;
Ok(buf.into_boxed_slice())
}
async fn write_message(&mut self, msg: BorrowedMessage<'_>) -> Result<(), ReadError> {
let mut buf = Vec::new();
msg.write_to(&mut buf)?;
self.tcp.write_all(&buf).await?;
Ok(())
}
}