deser-msgpack 0.10.0

MessagePack support for deser
Documentation
//! Raw MessagePack values.
use std::collections::BTreeMap;

use crate::common::{hex, to_hex};
use deser::adapters::Borrowed;
use deser::{Deserialize, Serialize};
use deser_json::RawJson;
use deser_msgpack::{RawMsgpack, SerializerConfig};

#[derive(Debug, Deserialize, Serialize)]
struct Envelope<'a> {
    kind: String,
    #[deser(as = Borrowed)]
    payload: RawMsgpack<'a>,
    after: u32,
}

/// `{"kind": "x", "payload": [1, 2], "after": 42}` with 1 encoded as uint 8.
const ENVELOPE: &str = "83 a4 6b696e64 a1 78 a7 7061796c6f6164 92 cc01 02 \
                        a5 6166746572 2a";
const PAYLOAD: &str = "92cc0102";

#[test]
fn test_borrowed() {
    let input = hex(ENVELOPE);
    let envelope: Envelope = deser_msgpack::from_slice(&input).unwrap();
    assert_eq!(envelope.kind, "x");
    assert_eq!(envelope.after, 42);
    // the encoding is kept as it is
    assert_eq!(to_hex(envelope.payload.as_bytes()), PAYLOAD);
    assert!(envelope.payload.is_borrowed());
    assert_eq!(envelope.payload.deserialize::<Vec<u64>>().unwrap(), [1, 2]);
}

#[test]
fn test_owned() {
    #[derive(Debug, Deserialize)]
    struct Owned {
        payload: RawMsgpack<'static>,
    }

    let input = hex(ENVELOPE);
    let owned: Owned = deser_msgpack::from_slice(&input).unwrap();
    drop(input);
    assert_eq!(to_hex(owned.payload.as_bytes()), PAYLOAD);
    assert!(!owned.payload.is_borrowed());
}

#[test]
fn test_serialize() {
    let input = hex(ENVELOPE);
    let envelope: Envelope = deser_msgpack::from_slice(&input).unwrap();
    // written as it is
    assert_eq!(deser_msgpack::to_vec(&envelope).unwrap(), input);
    // canonical output encodes the value again
    let canonical = SerializerConfig::builder()
        .canonical(true)
        .build()
        .to_vec(&envelope)
        .unwrap();
    assert!(
        to_hex(&canonical).contains("920102"),
        "{}",
        to_hex(&canonical)
    );
    // other formats write the value
    assert_eq!(
        deser_json::to_string(&envelope).unwrap(),
        r#"{"kind":"x","payload":[1,2],"after":42}"#
    );
}

#[test]
fn test_other_formats() {
    // raw MessagePack of JSON input is encoded as MessagePack
    let envelope: Envelope =
        deser_json::from_str(r#"{"kind": "x", "payload": [1,  2], "after": 42}"#).unwrap();
    assert_eq!(to_hex(envelope.payload.as_bytes()), "920102");

    // raw JSON of MessagePack input is encoded as JSON
    #[derive(Debug, Deserialize)]
    struct Json {
        payload: RawJson<'static>,
    }
    let json: Json = deser_msgpack::from_slice(&hex(ENVELOPE)).unwrap();
    assert_eq!(json.payload.get(), "[1,2]");
}

#[test]
fn test_containers() {
    #[derive(Debug, Deserialize)]
    struct Containers {
        items: Vec<RawMsgpack<'static>>,
        map: BTreeMap<String, RawMsgpack<'static>>,
        null: Option<RawMsgpack<'static>>,
        missing: Option<RawMsgpack<'static>>,
        empty: Vec<RawMsgpack<'static>>,
        after: Vec<u32>,
    }

    // {"items": [1, {"a": [2]}, ext(5, [0xff])], "map": {"a": bin(00)},
    //  "null": nil, "empty": [], "after": [1]}
    let input = hex(concat!(
        "85",
        "a5 6974656d73 93 01 81 a1 61 91 02 d4 05 ff",
        "a3 6d6170 81 a1 61 c4 01 00",
        "a4 6e756c6c c0",
        "a5 656d707479 90",
        "a5 6166746572 91 01",
    ));
    let value: Containers = deser_msgpack::from_slice(&input).unwrap();
    let items: Vec<String> = value.items.iter().map(|x| to_hex(x.as_bytes())).collect();
    assert_eq!(items, ["01", "81a1619102", "d405ff"]);
    assert_eq!(to_hex(value.map["a"].as_bytes()), "c40100");
    assert!(value.null.is_none());
    assert!(value.missing.is_none());
    assert!(value.empty.is_empty());
    assert_eq!(value.after, [1]);
}

#[test]
fn test_invalid() {
    for (payload, msg) in [
        // truncated
        ("92 01", "end of"),
        // the reserved byte
        ("c1", "reserved"),
        // invalid UTF-8 in a string
        ("a2 c328", "UTF-8"),
    ] {
        // {"kind": "x", "payload": ...}
        let input = hex(&format!("82 a4 6b696e64 a1 78 a7 7061796c6f6164 {payload}"));
        let err = deser_msgpack::from_slice::<Envelope>(&input).unwrap_err();
        assert!(err.to_string().contains(msg), "{payload}: {err}");
    }
}

#[test]
#[cfg(feature = "io")]
fn test_reader_in_chunks() {
    struct Chunked<'a>(&'a [u8]);

    impl std::io::Read for Chunked<'_> {
        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
            let len = buf.len().min(self.0.len()).min(1);
            buf[..len].copy_from_slice(&self.0[..len]);
            self.0 = &self.0[len..];
            Ok(len)
        }
    }

    #[derive(Debug, Deserialize)]
    struct Owned {
        payload: RawMsgpack<'static>,
        after: u32,
    }

    let input = hex(ENVELOPE);
    let value: Owned = deser_msgpack::from_reader(Chunked(&input)).unwrap();
    assert_eq!(to_hex(value.payload.as_bytes()), PAYLOAD);
    assert_eq!(value.after, 42);
}