use pack_io::{
Decoder, Deserialize, DeserializeView, SerialError, Serialize, decode, decode_view, encode,
};
fn round_trip<T>(value: T)
where
T: Serialize + Deserialize + PartialEq + core::fmt::Debug,
{
let bytes = encode(&value).expect("encode");
let back: T = decode(&bytes).expect("decode");
assert_eq!(back, value);
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Named {
id: u64,
text: String,
flag: bool,
}
#[test]
fn named_struct_round_trips() {
round_trip(Named {
id: 42,
text: "hello".into(),
flag: true,
});
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Tuple(u32, String, Vec<u8>);
#[test]
fn tuple_struct_round_trips() {
round_trip(Tuple(7, "world".into(), vec![1, 2, 3]));
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Unit;
#[test]
fn unit_struct_round_trips() {
round_trip(Unit);
let bytes = encode(&Unit).unwrap();
assert!(bytes.is_empty());
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Nested {
outer: u64,
inner: Named,
list: Vec<Tuple>,
}
#[test]
fn nested_struct_round_trips() {
round_trip(Nested {
outer: 1,
inner: Named {
id: 2,
text: "nested".into(),
flag: false,
},
list: vec![
Tuple(10, "a".into(), vec![]),
Tuple(20, "b".into(), vec![0xff]),
],
});
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Wrapper<T> {
inner: T,
label: String,
}
#[test]
fn generic_struct_round_trips() {
round_trip(Wrapper {
inner: 42_u64,
label: "generic".into(),
});
round_trip(Wrapper {
inner: vec![1u32, 2, 3],
label: "with-vec".into(),
});
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Shape {
Point,
Line(i32, i32),
Polygon {
vertices: Vec<(i32, i32)>,
closed: bool,
},
}
#[test]
fn enum_unit_variant_round_trips() {
round_trip(Shape::Point);
}
#[test]
fn enum_tuple_variant_round_trips() {
round_trip(Shape::Line(3, -7));
}
#[test]
fn enum_named_variant_round_trips() {
round_trip(Shape::Polygon {
vertices: vec![(0, 0), (1, 0), (1, 1), (0, 1)],
closed: true,
});
}
#[test]
fn enum_unknown_variant_index_is_rejected() {
let mut bytes = encode(&Shape::Line(1, 2)).unwrap();
bytes[0] = 5;
let err = decode::<Shape>(&bytes).expect_err("unknown variant rejected");
assert!(matches!(
err,
SerialError::UnknownVariant {
kind: "Shape",
index: 5
}
));
}
#[derive(Serialize)]
struct OwnedMsg {
id: u64,
text: String,
payload: Vec<u8>,
}
#[derive(Debug, PartialEq, DeserializeView)]
struct ViewMsg<'a> {
id: u64,
text: &'a str,
payload: &'a [u8],
}
#[test]
fn deserialize_view_borrows_str_and_bytes() {
let bytes = encode(&OwnedMsg {
id: 7,
text: "borrowed".into(),
payload: vec![10, 20, 30],
})
.unwrap();
let view: ViewMsg<'_> = decode_view(&bytes).unwrap();
assert_eq!(view.id, 7);
assert_eq!(view.text, "borrowed");
assert_eq!(view.payload, &[10, 20, 30]);
}
#[derive(Debug, PartialEq, DeserializeView)]
struct ViewWithOptional<'a> {
label: &'a str,
extra: Option<&'a str>,
count: u32,
}
#[derive(Serialize)]
struct OwnedWithOptional {
label: String,
extra: Option<String>,
count: u32,
}
#[derive(Debug, PartialEq, Serialize)]
enum OwnedEnvelope {
Empty,
Note(String),
Frame { header: String, body: Vec<u8> },
}
#[derive(Debug, PartialEq, DeserializeView)]
enum EnvelopeView<'a> {
Empty,
Note(&'a str),
Frame { header: &'a str, body: &'a [u8] },
}
#[test]
fn deserialize_view_decodes_enum_unit_variant() {
let bytes = encode(&OwnedEnvelope::Empty).unwrap();
let view: EnvelopeView<'_> = decode_view(&bytes).unwrap();
assert_eq!(view, EnvelopeView::Empty);
}
#[test]
fn deserialize_view_decodes_enum_tuple_variant_with_borrowed_str() {
let bytes = encode(&OwnedEnvelope::Note("ping".into())).unwrap();
let view: EnvelopeView<'_> = decode_view(&bytes).unwrap();
assert_eq!(view, EnvelopeView::Note("ping"));
}
#[test]
fn deserialize_view_decodes_enum_named_variant_with_borrowed_fields() {
let bytes = encode(&OwnedEnvelope::Frame {
header: "x-trace".into(),
body: vec![1, 2, 3, 4, 5],
})
.unwrap();
let view: EnvelopeView<'_> = decode_view(&bytes).unwrap();
assert_eq!(
view,
EnvelopeView::Frame {
header: "x-trace",
body: &[1, 2, 3, 4, 5],
}
);
}
#[test]
fn deserialize_view_enum_rejects_unknown_variant_index() {
let mut bytes = encode(&OwnedEnvelope::Note("hi".into())).unwrap();
bytes[0] = 7; let err = decode_view::<EnvelopeView<'_>>(&bytes).expect_err("unknown variant rejected");
assert!(matches!(
err,
SerialError::UnknownVariant {
kind: "EnvelopeView",
index: 7
}
));
}
#[test]
fn deserialize_view_handles_option_of_borrowed() {
let bytes = encode(&OwnedWithOptional {
label: "hdr".into(),
extra: Some("present".into()),
count: 9,
})
.unwrap();
let view: ViewWithOptional<'_> = decode_view(&bytes).unwrap();
assert_eq!(view.label, "hdr");
assert_eq!(view.extra, Some("present"));
assert_eq!(view.count, 9);
let bytes = encode(&OwnedWithOptional {
label: "hdr".into(),
extra: None,
count: 0,
})
.unwrap();
let view: ViewWithOptional<'_> = decode_view(&bytes).unwrap();
assert_eq!(view.extra, None);
}
#[derive(Serialize)]
struct DerivedTriple {
a: u64,
b: String,
c: bool,
}
struct HandRolledTriple {
a: u64,
b: String,
c: bool,
}
impl Serialize for HandRolledTriple {
fn serialize<E: pack_io::Encode + ?Sized>(&self, enc: &mut E) -> pack_io::Result<()> {
self.a.serialize(enc)?;
self.b.serialize(enc)?;
self.c.serialize(enc)
}
}
#[test]
fn derived_serialize_produces_same_bytes_as_hand_rolled() {
let derived = DerivedTriple {
a: 100,
b: "match".into(),
c: true,
};
let hand = HandRolledTriple {
a: 100,
b: "match".into(),
c: true,
};
assert_eq!(encode(&derived).unwrap(), encode(&hand).unwrap());
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct OwnedSimple {
a: u64,
b: String,
}
#[derive(Debug, PartialEq, DeserializeView)]
struct ViewSimple<'a> {
a: u64,
b: &'a str,
}
#[test]
fn view_and_owning_decode_agree() {
let owned = OwnedSimple {
a: 99,
b: "string".into(),
};
let bytes = encode(&owned).unwrap();
let back_owned: OwnedSimple = decode(&bytes).unwrap();
let back_view: ViewSimple<'_> = decode_view(&bytes).unwrap();
assert_eq!(back_owned.a, back_view.a);
assert_eq!(back_owned.b.as_str(), back_view.b);
}
#[test]
fn decoder_read_length_prefixed_borrowed_zero_copies() {
let bytes = encode(&"manual borrow").unwrap();
let mut dec = Decoder::new(&bytes);
let slice = dec.read_length_prefixed_borrowed().unwrap();
let text = core::str::from_utf8(slice).unwrap();
assert_eq!(text, "manual borrow");
}