use serde::Deserialize;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum FrameError {
#[error("postcard decode failed: {0}")]
Decode(postcard::Error),
#[error("frame has {0} trailing bytes")]
Trailing(usize),
}
pub fn decode_exact<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T, FrameError> {
let (value, rest) = postcard::take_from_bytes(bytes).map_err(FrameError::Decode)?;
match rest.len() {
0 => Ok(value),
n => Err(FrameError::Trailing(n)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
use serde::Serialize;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Two {
a: f32,
b: f32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Three {
a: f32,
b: f32,
c: f32,
}
fn encode<T: Serialize>(value: &T) -> Vec<u8> {
postcard::to_allocvec(value).expect("serialize")
}
#[test]
fn an_exact_frame_decodes() {
let bytes = encode(&Two { a: 1.0, b: 2.0 });
assert_eq!(decode_exact::<Two>(&bytes), Ok(Two { a: 1.0, b: 2.0 }));
}
#[test]
fn a_frame_with_a_dropped_field_is_rejected() {
let bytes = encode(&Three {
a: 1.0,
b: 2.0,
c: 3.0,
});
assert!(postcard::from_bytes::<Two>(&bytes).is_ok());
assert_eq!(decode_exact::<Two>(&bytes), Err(FrameError::Trailing(4)));
}
#[test]
fn a_frame_missing_an_added_field_is_rejected() {
let bytes = encode(&Two { a: 1.0, b: 2.0 });
assert_eq!(
decode_exact::<Three>(&bytes),
Err(FrameError::Decode(
postcard::Error::DeserializeUnexpectedEnd
))
);
}
#[test]
fn an_empty_frame_is_rejected() {
assert!(decode_exact::<Two>(&[]).is_err());
}
}