use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{collections::BTreeMap, fmt::Debug, marker::PhantomData};
use postbag::{
cfg::{Full, Slim, Version},
deserialize, serialize,
};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct One<T> {
#[serde(rename = "_0")]
v: T,
}
impl<T> One<T> {
fn new(v: T) -> Self {
Self { v }
}
}
#[track_caller]
fn both<T>(value: &T) -> (Vec<u8>, Vec<u8>)
where
T: Serialize + DeserializeOwned + Debug + PartialEq,
{
let mut written = Vec::new();
for version in [Version::Postbag1, Version::Postbag0_4] {
let cfg = Full::new().with_version(version);
let mut bytes = Vec::new();
serialize(cfg, &mut bytes, value).expect("serialization failed");
let back: T = deserialize(cfg, bytes.as_slice()).expect("deserialization failed");
assert_eq!(back, *value, "value did not survive {version:?}");
written.push(bytes);
}
(written.remove(0), written.remove(0))
}
#[track_caller]
fn unaffected<T>(value: &T)
where
T: Serialize + DeserializeOwned + Debug + PartialEq,
{
let (v1, v0_4) = both(value);
assert_eq!(v1, v0_4, "the version changed the bytes of a value it should not reach");
}
#[test]
fn string_field() {
let (v1, v0_4) = both(&One::new("temp".to_string()));
assert_eq!(v1, b"\x01\x41\x04temp");
assert_eq!(v0_4, b"\x01\x41\x05\x04temp");
}
#[test]
fn empty_string_field() {
let (v1, v0_4) = both(&One::new(String::new()));
assert_eq!(v1, b"\x01\x41\x00");
assert_eq!(v0_4, b"\x01\x41\x01\x00");
}
#[test]
fn string_length_is_bytes_not_chars() {
let (v1, v0_4) = both(&One::new("°C".to_string()));
assert_eq!(v1, b"\x01\x41\x03\xc2\xb0C");
assert_eq!(v0_4, b"\x01\x41\x04\x03\xc2\xb0C");
}
#[test]
fn bytes_field() {
let (v1, v0_4) = both(&One::new(serde_bytes::ByteBuf::from(vec![1u8, 2, 3])));
assert_eq!(v1, b"\x01\x41\x03\x01\x02\x03");
assert_eq!(v0_4, b"\x01\x41\x04\x03\x01\x02\x03");
}
#[test]
fn char_field() {
let (v1, v0_4) = both(&One::new('°'));
assert_eq!(v1, b"\x01\x41\x02\xc2\xb0");
assert_eq!(v0_4, b"\x01\x41\x03\x02\xc2\xb0");
}
#[test]
fn option_passes_it_on() {
let (v1, v0_4) = both(&One::new(Some("hi".to_string())));
assert_eq!(v1, b"\x01\x41\x03\x01hi");
assert_eq!(v0_4, b"\x01\x41\x04\x01\x02hi");
unaffected(&One::new(Option::<String>::None));
}
#[test]
fn nested_option_passes_it_on() {
let (v1, v0_4) = both(&One::new(Some(Some("hi".to_string()))));
assert_eq!(v1, b"\x01\x41\x04\x01\x01hi");
assert_eq!(v0_4, b"\x01\x41\x05\x01\x01\x02hi");
let (v1, v0_4) = both(&One::new(Some(Option::<String>::None)));
assert_eq!(v1, b"\x01\x41\x02\x01\x00");
assert_eq!(v0_4, v1);
}
#[test]
fn newtype_struct_passes_it_on() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Name(String);
let (v1, v0_4) = both(&One::new(Name("hi".to_string())));
assert_eq!(v1, b"\x01\x41\x02hi");
assert_eq!(v0_4, b"\x01\x41\x03\x02hi");
}
#[test]
fn a_nested_struct_leaves_out_its_field_count() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Inner {
#[serde(rename = "_0")]
text: String,
}
let (v1, v0_4) = both(&One::new(Inner { text: "hi".to_string() }));
assert_eq!(v1, b"\x01\x41\x04\x41\x02hi");
assert_eq!(v0_4, b"\x01\x41\x06\x01\x41\x03\x02hi");
}
#[test]
fn a_top_level_struct_keeps_its_field_count() {
let mut bytes = Vec::new();
serialize(Full::new(), &mut bytes, &One::new(7u8)).unwrap();
assert_eq!(bytes, b"\x01\x41\x01\x07");
}
#[test]
fn a_struct_in_a_sequence_keeps_its_field_count() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Inner {
#[serde(rename = "_0")]
n: u8,
}
let (v1, v0_4) = both(&One::new(vec![Inner { n: 7 }, Inner { n: 8 }]));
assert_eq!(v1, b"\x01\x41\x09\x02\x01\x41\x01\x07\x01\x41\x01\x08");
assert_eq!(v0_4, v1);
}
#[test]
fn a_sequence_keeps_its_count() {
let (v1, v0_4) = both(&One::new(vec!["a".to_string(), "bb".to_string()]));
assert_eq!(v1, b"\x01\x41\x06\x02\x01a\x02bb");
assert_eq!(v0_4, v1);
let (v1, _) = both(&One::new(vec![(), (), ()]));
assert_eq!(v1, b"\x01\x41\x01\x03");
}
#[test]
fn a_map_keeps_its_count() {
let (v1, v0_4) = both(&One::new(BTreeMap::<(), ()>::new()));
assert_eq!(v1, b"\x01\x41\x01\x00");
assert_eq!(v0_4, v1);
let (v1, v0_4) = both(&One::new(BTreeMap::from([((), ())])));
assert_eq!(v1, b"\x01\x41\x01\x01");
assert_eq!(v0_4, v1);
}
#[test]
fn elements_and_members_keep_their_lengths() {
unaffected(&One::new(vec!["a".to_string(), "bb".to_string()]));
unaffected(&One::new(("a".to_string(), "bb".to_string())));
unaffected(&One::new(("a".to_string(), 7u32)));
unaffected(&One::new((7u32, "a".to_string())));
unaffected(&One::new([[1u8, 2], [3, 4]]));
unaffected(&One::new(BTreeMap::from([("k".to_string(), "v".to_string())])));
unaffected(&One::new(vec![Some("a".to_string()), None]));
}
#[test]
fn an_enum_variant_passes_it_on() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
enum Unit {
#[serde(rename = "_0")]
Celsius,
#[serde(rename = "_1")]
Other(String),
#[serde(rename = "_2")]
Scaled {
#[serde(rename = "_0")]
by: u8,
},
}
let (v1, v0_4) = both(&One::new(Unit::Other("K".to_string())));
assert_eq!(v1, b"\x01\x41\x02\x42K");
assert_eq!(v0_4, b"\x01\x41\x03\x42\x01K");
let (v1, v0_4) = both(&One::new(Unit::Scaled { by: 7 }));
assert_eq!(v1, b"\x01\x41\x04\x43\x41\x01\x07");
assert_eq!(v0_4, b"\x01\x41\x05\x43\x01\x41\x01\x07");
let (v1, v0_4) = both(&One::new(Unit::Celsius));
assert_eq!(v1, b"\x01\x41\x01\x41");
assert_eq!(v0_4, v1);
}
#[test]
fn a_value_of_its_own_keeps_its_length() {
let mut bytes = Vec::new();
serialize(Full::new(), &mut bytes, &"temp".to_string()).unwrap();
assert_eq!(bytes, b"\x04temp");
unaffected(&"temp".to_string());
}
#[test]
fn nothing_is_read_beyond_the_value() {
for value in ["temp".to_string(), String::new()] {
let mut bytes = Vec::new();
serialize(Full::new(), &mut bytes, &value).unwrap();
bytes.extend_from_slice(b"and more");
let mut input = bytes.as_slice();
let back: String = deserialize(Full::new(), &mut input).unwrap();
assert_eq!(back, value);
assert_eq!(input, b"and more", "the reader was advanced past the value");
}
let mut bytes = Vec::new();
serialize(Full::new(), &mut bytes, &One::new("temp".to_string())).unwrap();
bytes.extend_from_slice(b"and more");
let mut input = bytes.as_slice();
let back: One<String> = deserialize(Full::new(), &mut input).unwrap();
assert_eq!(back.v, "temp");
assert_eq!(input, b"and more", "the reader was advanced past the value");
}
#[test]
fn slim_is_unaffected() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Reading {
sensor: u32,
label: String,
}
let value = Reading { sensor: 300, label: "temp".to_string() };
let mut written = Vec::new();
for version in [Version::Postbag1, Version::Postbag0_4] {
let cfg = Slim::new().with_version(version);
let mut bytes = Vec::new();
serialize(cfg, &mut bytes, &value).unwrap();
let back: Reading = deserialize(cfg, bytes.as_slice()).unwrap();
assert_eq!(back, value);
written.push(bytes);
}
assert_eq!(written[0], written[1]);
}
const MAX_BLOCK: usize = u16::MAX as usize;
#[test]
fn a_string_longer_than_a_block() {
for len in [MAX_BLOCK - 1, MAX_BLOCK, MAX_BLOCK + 1, 2 * MAX_BLOCK, 2 * MAX_BLOCK + 1] {
let value = One::new("x".repeat(len));
let (v1, v0_4) = both(&value);
assert!(v1.len() > len, "at {len} bytes");
assert!(v0_4.len() > v1.len(), "at {len} bytes");
}
}
#[test]
fn bytes_longer_than_a_block() {
for len in [MAX_BLOCK, MAX_BLOCK + 1] {
let value = One::new(serde_bytes::ByteBuf::from(vec![0xab; len]));
both(&value);
}
}
#[test]
fn no_field_is_empty_in_full() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Units {
#[serde(rename = "_0")]
a: (),
#[serde(rename = "_1")]
b: u8,
#[serde(rename = "_2")]
c: (),
}
let (v1, v0_4) = both(&One::new(Units { a: (), b: 7, c: () }));
assert_eq!(v1, b"\x01\x41\x07\x41\x00\x42\x01\x07\x43\x00");
assert_eq!(v0_4, b"\x01\x41\x08\x03\x41\x00\x42\x01\x07\x43\x00");
}
#[test]
fn slim_needs_its_count_because_a_field_can_be_empty() {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Units {
a: (),
b: (),
}
let bytes = postbag::to_vec(Slim::new(), &Units { a: (), b: () }).unwrap();
let back: Units = deserialize(Slim::new(), bytes.as_slice()).unwrap();
assert_eq!(back, Units { a: (), b: () });
assert_eq!(bytes, b"\x02\x00", "the count is all there is");
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Marked {
a: u32,
marker: PhantomData<u8>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct Unmarked {
a: u32,
}
let marked = postbag::to_vec(Slim::new(), &Marked { a: 300, marker: PhantomData }).unwrap();
let unmarked = postbag::to_vec(Slim::new(), &Unmarked { a: 300 }).unwrap();
assert_eq!(marked, b"\x02\x02\xac\x02");
assert_eq!(unmarked, b"\x01\x02\xac\x02");
assert_eq!(marked[1..], unmarked[1..], "only the count tells them apart");
let err = postbag::from_slice::<Marked, _>(Slim::new(), unmarked.as_slice()).unwrap_err();
assert!(err.to_string().contains("invalid length"), "got {err}");
}
#[test]
fn versions_have_stable_bytes() {
assert_eq!(u8::from(Version::Postbag0_4), 0);
assert_eq!(u8::from(Version::Postbag1), 1);
assert_eq!(Version::try_from(0u8).unwrap(), Version::Postbag0_4);
assert_eq!(Version::try_from(1u8).unwrap(), Version::Postbag1);
}
#[test]
fn versions_order_with_their_bytes() {
assert!(Version::Postbag0_4 < Version::Postbag1);
assert!(u8::from(Version::Postbag0_4) < u8::from(Version::Postbag1));
assert_eq!(Version::Postbag1.min(Version::Postbag0_4), Version::Postbag0_4);
}
#[test]
fn an_unknown_byte_names_no_version() {
let err = Version::try_from(200u8).unwrap_err();
assert_eq!(err, postbag::cfg::UnknownVersion(200));
assert_eq!(err.to_string(), "unknown Postbag data format version 200");
}