concinnity_core/blob/
frame.rs1use serde::Deserialize;
13use thiserror::Error;
14
15#[derive(Debug, Clone, PartialEq, Eq, Error)]
17pub enum FrameError {
18 #[error("postcard decode failed: {0}")]
20 Decode(postcard::Error),
21 #[error("frame has {0} trailing bytes")]
24 Trailing(usize),
25}
26
27pub fn decode_exact<'a, T: Deserialize<'a>>(bytes: &'a [u8]) -> Result<T, FrameError> {
30 let (value, rest) = postcard::take_from_bytes(bytes).map_err(FrameError::Decode)?;
31 match rest.len() {
32 0 => Ok(value),
33 n => Err(FrameError::Trailing(n)),
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use alloc::vec::Vec;
41 use serde::Serialize;
42
43 #[derive(Debug, PartialEq, Serialize, Deserialize)]
44 struct Two {
45 a: f32,
46 b: f32,
47 }
48
49 #[derive(Debug, PartialEq, Serialize, Deserialize)]
50 struct Three {
51 a: f32,
52 b: f32,
53 c: f32,
54 }
55
56 fn encode<T: Serialize>(value: &T) -> Vec<u8> {
57 postcard::to_allocvec(value).expect("serialize")
58 }
59
60 #[test]
61 fn an_exact_frame_decodes() {
62 let bytes = encode(&Two { a: 1.0, b: 2.0 });
63 assert_eq!(decode_exact::<Two>(&bytes), Ok(Two { a: 1.0, b: 2.0 }));
64 }
65
66 #[test]
69 fn a_frame_with_a_dropped_field_is_rejected() {
70 let bytes = encode(&Three {
71 a: 1.0,
72 b: 2.0,
73 c: 3.0,
74 });
75 assert!(postcard::from_bytes::<Two>(&bytes).is_ok());
76 assert_eq!(decode_exact::<Two>(&bytes), Err(FrameError::Trailing(4)));
77 }
78
79 #[test]
82 fn a_frame_missing_an_added_field_is_rejected() {
83 let bytes = encode(&Two { a: 1.0, b: 2.0 });
84 assert_eq!(
85 decode_exact::<Three>(&bytes),
86 Err(FrameError::Decode(
87 postcard::Error::DeserializeUnexpectedEnd
88 ))
89 );
90 }
91
92 #[test]
93 fn an_empty_frame_is_rejected() {
94 assert!(decode_exact::<Two>(&[]).is_err());
95 }
96}