use crate::error::{Result, SpiceError};
use crate::protocol::SpiceDataHeader;
use crate::transport::Transport;
use binrw::{BinRead, BinWrite};
use std::io::Cursor;
pub trait SpiceWireFormat: Sized {
fn wire_size() -> usize;
fn write_to_wire(&self, buf: &mut Vec<u8>) -> Result<()>;
fn read_from_wire(buf: &[u8]) -> Result<Self>;
}
pub trait WriteWire {
fn write_u8(&mut self, val: u8);
fn write_u16_le(&mut self, val: u16);
fn write_u32_le(&mut self, val: u32);
fn write_u64_le(&mut self, val: u64);
fn write_bytes(&mut self, bytes: &[u8]);
}
impl WriteWire for Vec<u8> {
fn write_u8(&mut self, val: u8) {
self.push(val);
}
fn write_u16_le(&mut self, val: u16) {
self.extend_from_slice(&val.to_le_bytes());
}
fn write_u32_le(&mut self, val: u32) {
self.extend_from_slice(&val.to_le_bytes());
}
fn write_u64_le(&mut self, val: u64) {
self.extend_from_slice(&val.to_le_bytes());
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.extend_from_slice(bytes);
}
}
pub trait ReadWire {
fn read_u8(&self, offset: usize) -> Result<u8>;
fn read_u16_le(&self, offset: usize) -> Result<u16>;
fn read_u32_le(&self, offset: usize) -> Result<u32>;
fn read_u64_le(&self, offset: usize) -> Result<u64>;
fn read_bytes(&self, offset: usize, len: usize) -> Result<&[u8]>;
}
impl ReadWire for [u8] {
fn read_u8(&self, offset: usize) -> Result<u8> {
if offset >= self.len() {
return Err(crate::error::SpiceError::Protocol(
"Buffer too small for u8".into(),
));
}
Ok(self[offset])
}
fn read_u16_le(&self, offset: usize) -> Result<u16> {
if offset + 2 > self.len() {
return Err(crate::error::SpiceError::Protocol(
"Buffer too small for u16".into(),
));
}
Ok(u16::from_le_bytes([self[offset], self[offset + 1]]))
}
fn read_u32_le(&self, offset: usize) -> Result<u32> {
if offset + 4 > self.len() {
return Err(crate::error::SpiceError::Protocol(
"Buffer too small for u32".into(),
));
}
Ok(u32::from_le_bytes([
self[offset],
self[offset + 1],
self[offset + 2],
self[offset + 3],
]))
}
fn read_u64_le(&self, offset: usize) -> Result<u64> {
if offset + 8 > self.len() {
return Err(crate::error::SpiceError::Protocol(
"Buffer too small for u64".into(),
));
}
Ok(u64::from_le_bytes([
self[offset],
self[offset + 1],
self[offset + 2],
self[offset + 3],
self[offset + 4],
self[offset + 5],
self[offset + 6],
self[offset + 7],
]))
}
fn read_bytes(&self, offset: usize, len: usize) -> Result<&[u8]> {
if offset + len > self.len() {
return Err(crate::error::SpiceError::Protocol(
"Buffer too small for bytes".into(),
));
}
Ok(&self[offset..offset + len])
}
}
pub async fn read_message(
transport: &mut Box<dyn Transport>,
) -> Result<(SpiceDataHeader, Vec<u8>)> {
let mut header_buf = vec![0u8; 18];
let mut total_read = 0;
while total_read < 18 {
let n = transport
.read(&mut header_buf[total_read..])
.await
.map_err(SpiceError::Io)?;
if n == 0 {
return Err(SpiceError::Protocol(
"Connection closed while reading header".to_string(),
));
}
total_read += n;
}
let mut cursor = Cursor::new(&header_buf);
let header = SpiceDataHeader::read(&mut cursor)
.map_err(|e| SpiceError::Protocol(format!("Failed to parse header: {}", e)))?;
let mut data = vec![0u8; header.msg_size as usize];
total_read = 0;
while total_read < header.msg_size as usize {
let n = transport
.read(&mut data[total_read..])
.await
.map_err(SpiceError::Io)?;
if n == 0 {
return Err(SpiceError::Protocol(
"Connection closed while reading message data".to_string(),
));
}
total_read += n;
}
Ok((header, data))
}
pub async fn write_message(
transport: &mut Box<dyn Transport>,
header: &SpiceDataHeader,
data: &[u8],
) -> Result<()> {
let mut header_bytes = Vec::new();
let mut cursor = Cursor::new(&mut header_bytes);
header
.write(&mut cursor)
.map_err(|e| SpiceError::Protocol(format!("Failed to serialize header: {}", e)))?;
transport
.write_all(&header_bytes)
.await
.map_err(SpiceError::Io)?;
transport.write_all(data).await.map_err(SpiceError::Io)?;
transport.flush().await.map_err(SpiceError::Io)?;
Ok(())
}