use std::io;
use crate::{Readable, Writeable};
pub trait TypePrefixedPayload<const N: usize>:
Readable + Writeable + Clone + std::fmt::Debug
{
const TYPE: Option<[u8; N]>;
fn written_size(&self) -> usize;
fn payload_written_size(&self) -> usize {
match Self::TYPE {
Some(_) => self.written_size() + N,
None => self.written_size(),
}
}
fn read_payload<R: io::Read>(reader: &mut R) -> Result<Self, io::Error> {
match Self::TYPE {
Some(id) => {
if id != <[u8; N]>::read(reader)? {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid payload type",
));
}
Readable::read(reader)
}
None => Readable::read(reader),
}
}
fn read_slice(buf: &[u8]) -> Result<Self, io::Error> {
let buf = &mut &buf[..];
let out = Self::read_payload(buf)?;
if buf.is_empty() {
Ok(out)
} else {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid payload length",
))
}
}
fn write_payload<W: io::Write>(&self, writer: &mut W) -> Result<(), io::Error> {
match Self::TYPE {
Some(id) => {
id.write(writer)?;
Writeable::write(self, writer)
}
None => Writeable::write(self, writer),
}
}
fn to_vec(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.payload_written_size());
self.write_payload(&mut buf).expect("no alloc failure");
buf
}
}
#[cfg(test)]
mod test {
use crate::{Readable, TypePrefixedPayload, Writeable, WriteableBytes};
use hex_literal::hex;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NineteenBytes([u8; 19]);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message {
pub a: u32,
pub b: NineteenBytes,
pub c: WriteableBytes<u32>,
pub d: [u64; 4],
pub e: bool,
}
impl TypePrefixedPayload<1> for Message {
const TYPE: Option<[u8; 1]> = Some([69]);
fn written_size(&self) -> usize {
88
}
}
impl Readable for Message {
fn read<R>(reader: &mut R) -> std::io::Result<Self>
where
Self: Sized,
R: std::io::Read,
{
Ok(Self {
a: Readable::read(reader)?,
b: NineteenBytes(Readable::read(reader)?),
c: Readable::read(reader)?,
d: Readable::read(reader)?,
e: Readable::read(reader)?,
})
}
}
impl Writeable for Message {
fn write<W>(&self, writer: &mut W) -> std::io::Result<()>
where
W: std::io::Write,
{
self.a.write(writer)?;
self.b.0.write(writer)?;
self.c.write(writer)?;
self.d.write(writer)?;
self.e.write(writer)?;
Ok(())
}
}
#[test]
fn to_vec_payload() {
let msg = Message {
a: 420,
b: NineteenBytes(hex!("ba5edba5edba5edba5edba5edba5edba5edba5")),
c: b"Somebody set us up the bomb.".to_vec().try_into().unwrap(),
d: [0x45; 4],
e: true,
};
let mut encoded = msg.to_vec();
assert_eq!(encoded, hex!("45000001a4ba5edba5edba5edba5edba5edba5edba5edba50000001c536f6d65626f6479207365742075732075702074686520626f6d622e000000000000004500000000000000450000000000000045000000000000004501"));
assert_eq!(encoded.capacity(), 1 + msg.written_size());
assert_eq!(encoded.capacity(), encoded.len());
let mut cursor = std::io::Cursor::new(&mut encoded);
let decoded = Message::read_payload(&mut cursor).unwrap();
assert_eq!(msg, decoded);
}
#[test]
fn invalid_length() {
let encoded = hex!("45000001a4ba5edba5edba5edba5edba5edba5edba5edba50000001c536f6d65626f6479207365742075732075702074686520626f6d622e00000000000000450000000000000045000000000000004500000000000000450169");
assert!(matches!(
Message::read_slice(&encoded).unwrap_err().kind(),
std::io::ErrorKind::InvalidData,
));
}
#[test]
fn read_slice() {
let encoded = hex!("45000001a4ba5edba5edba5edba5edba5edba5edba5edba50000001c536f6d65626f6479207365742075732075702074686520626f6d622e000000000000004500000000000000450000000000000045000000000000004501");
let expected = Message {
a: 420,
b: NineteenBytes(hex!("ba5edba5edba5edba5edba5edba5edba5edba5")),
c: b"Somebody set us up the bomb.".to_vec().try_into().unwrap(),
d: [0x45; 4],
e: true,
};
let decoded = Message::read_slice(&encoded).unwrap();
assert_eq!(decoded, expected);
}
}