use bitstream_io::{BigEndian, BitReader, BitWriter, LittleEndian};
use crate::{BitRead, BitWrite, ByteOrder, Error};
use core::any::Any;
use std::io;
pub trait Protocol: Sized {
fn read(
read: &mut dyn BitRead,
byte_order: ByteOrder,
ctx: &mut dyn Any,
) -> Result<Self, Error>;
fn write(
&self,
write: &mut dyn BitWrite,
byte_order: ByteOrder,
ctx: &mut dyn Any,
) -> Result<(), Error>;
fn from_bytes(bytes: &[u8], byte_order: ByteOrder) -> Result<Self, Error> {
Self::from_bytes_ctx(bytes, byte_order, &mut ())
}
fn from_bytes_ctx(
bytes: &[u8],
byte_order: ByteOrder,
ctx: &mut dyn Any,
) -> Result<Self, Error> {
match byte_order {
crate::ByteOrder::LittleEndian => {
let mut buffer = BitReader::endian(io::Cursor::new(bytes), LittleEndian);
Self::read(&mut buffer, byte_order, ctx)
}
crate::ByteOrder::BigEndian => {
let mut buffer = BitReader::endian(io::Cursor::new(bytes), BigEndian);
Self::read(&mut buffer, byte_order, ctx)
}
}
}
fn bytes(&self, byte_order: ByteOrder) -> Result<Vec<u8>, Error> {
self.bytes_ctx(byte_order, &mut ())
}
fn bytes_ctx(&self, byte_order: ByteOrder, ctx: &mut dyn Any) -> Result<Vec<u8>, Error> {
let mut data = Vec::new();
match byte_order {
crate::ByteOrder::LittleEndian => {
let mut writer = BitWriter::endian(&mut data, LittleEndian);
self.write(&mut writer, byte_order, ctx)?;
writer.byte_align()?;
}
crate::ByteOrder::BigEndian => {
let mut writer = BitWriter::endian(&mut data, BigEndian);
self.write(&mut writer, byte_order, ctx)?;
writer.byte_align()?;
}
};
Ok(data)
}
}
#[cfg(test)]
macro_rules! test_protocol {
($t:ty => [$bytes:expr, $value:expr]) => {
#[test]
fn read_protocol() {
let bytes: &[u8] = $bytes.as_slice();
assert_eq!(
<$t as crate::Protocol>::read(
&mut bitstream_io::BitReader::endian(bytes, bitstream_io::BigEndian),
crate::ByteOrder::BigEndian,
&mut ()
)
.unwrap(),
$value
)
}
#[test]
fn write_protocol() {
let mut buffer: Vec<u8> = Vec::new();
let value: $t = $value;
crate::Protocol::write(
&value,
&mut bitstream_io::BitWriter::endian(&mut buffer, bitstream_io::BigEndian),
crate::ByteOrder::BigEndian,
&mut (),
)
.unwrap();
assert_eq!(buffer.as_slice(), $bytes)
}
};
}