deser 0.9.0

An experimental serialization and deserialization library for Rust
Documentation
use std::borrow::Cow;
use std::collections::BTreeMap;

use deser::adapters::{As, Borrowed};
use deser::adapters::{Base64Url, BytesEncoding, BytesFallback, IntSeq};
use deser::de::{DeserializeDriver, DeserializeOwned};
use deser::ser::SerializeDriver;
use deser::{Atom, BytesFormat, Deserialize, Error, ErrorKind, Event, Serialize};

/// Removes the length from container starts, the tests are not about it.
fn without_len(event: deser::Event<'static>) -> deser::Event<'static> {
    match event {
        deser::Event::MapStart(shape) => {
            deser::Event::MapStart(deser::ContainerShape::new().with_order(shape.order()))
        }
        deser::Event::SeqStart(shape) => {
            deser::Event::SeqStart(deser::ContainerShape::new().with_order(shape.order()))
        }
        event => event,
    }
}

fn deserialize<T: DeserializeOwned>(events: Vec<Event<'_>>) -> Result<T, Error> {
    deserialize_with(events, None)
}

fn deserialize_with<T: DeserializeOwned>(
    events: Vec<Event<'_>>,
    format: Option<BytesFormat>,
) -> Result<T, Error> {
    let mut out = None;
    {
        let mut driver = DeserializeDriver::new(&mut out);
        if let Some(format) = format {
            format.set(driver.state_mut());
        }
        for event in events {
            driver.emit(event)?;
        }
    }
    Ok(out.unwrap())
}

/// Serializes a value into events with the requested bytes formats.
fn serialize(value: &dyn Serialize) -> Vec<(Event<'static>, Option<BytesFormat>)> {
    let mut events = Vec::new();
    SerializeDriver::new(value)
        .drive(|event, _| {
            // the fallback is reported separately
            let (event, format) = match event {
                Event::Atom(Atom::Bytes(bytes)) => {
                    let format = bytes.fallback.copied();
                    (
                        Event::Atom(Atom::Bytes(deser::Bytes::new(bytes.into_data()))),
                        format,
                    )
                }
                event => (event, None),
            };
            events.push((without_len(event.to_static()), format));
            Ok(())
        })
        .unwrap();
    events
}

/// A custom encoding: decimal numbers separated by dots.
struct Dotted;

impl BytesEncoding for Dotted {
    const NAME: &'static str = "dotted";

    fn encode(bytes: &[u8], out: &mut String) {
        let parts = bytes.iter().map(|x| x.to_string()).collect::<Vec<_>>();
        out.push_str(&parts.join("."));
    }

    fn decode(s: &str) -> Result<Vec<u8>, Error> {
        s.split('.')
            .map(|x| {
                x.parse()
                    .map_err(|_| Error::new(ErrorKind::Unexpected, "invalid byte"))
            })
            .collect()
    }
}

fn bytes(value: &[u8]) -> Event<'static> {
    Event::Atom(Atom::Bytes(deser::Bytes::new(Cow::Owned(value.to_vec()))))
}

#[test]
fn test_bytes_from_strings() {
    let value: Vec<u8> = deserialize(vec!["Af8=".into()]).unwrap();
    assert_eq!(value, [1, 255]);
    let value: Vec<u8> = deserialize(vec!["-_8".into()]).unwrap();
    assert_eq!(value, [251, 255]);
    let value: [u8; 2] = deserialize(vec!["Af8".into()]).unwrap();
    assert_eq!(value, [1, 255]);
    let value: Cow<'static, [u8]> = deserialize(vec!["Af8=".into()]).unwrap();
    assert_eq!(&*value, [1, 255]);
    let value: Option<Vec<u8>> = deserialize(vec!["".into()]).unwrap();
    assert_eq!(value, Some(vec![]));

    // sequences still work
    let value: Vec<u8> = deserialize(vec![
        Event::seq_start(),
        1u64.into(),
        255u64.into(),
        Event::SeqEnd,
    ])
    .unwrap();
    assert_eq!(value, [1, 255]);

    let err = deserialize::<Vec<u8>>(vec!["A".into()]).unwrap_err();
    assert_eq!(err.to_string(), "Unexpected: invalid base64 string");
    let err = deserialize::<[u8; 3]>(vec!["Af8=".into()]).unwrap_err();
    assert_eq!(err.kind(), ErrorKind::WrongLength);

    // only bytes accept strings
    assert!(deserialize::<Vec<u16>>(vec!["Af8=".into()]).is_err());
    assert!(deserialize::<[u16; 2]>(vec!["Af8=".into()]).is_err());

    // bytes cannot be borrowed from strings
    let mut out = None::<&[u8]>;
    let mut driver = DeserializeDriver::new(&mut out);
    let err = driver.emit_borrowed("Af8=").unwrap_err();
    assert!(err.to_string().contains("cannot be borrowed"));
}

#[test]
fn test_borrowed_bytes_from_strings() {
    let mut out = None::<As<Cow<[u8]>, Borrowed>>;
    {
        let mut driver = DeserializeDriver::new(&mut out);
        driver.emit_borrowed("Af8=").unwrap();
    }
    assert_eq!(&**out.unwrap(), [1, 255]);
}

#[test]
fn test_bytes_format_in_state() {
    let dotted = Some(BytesFormat::encoded::<Dotted>());
    let value: Vec<u8> = deserialize_with(vec!["1.255".into()], dotted).unwrap();
    assert_eq!(value, [1, 255]);
    let value: [u8; 2] = deserialize_with(vec!["1.255".into()], dotted).unwrap();
    assert_eq!(value, [1, 255]);
    let value: Cow<'static, [u8]> = deserialize_with(vec!["1.255".into()], dotted).unwrap();
    assert_eq!(&*value, [1, 255]);
    let value: Vec<u8> = deserialize_with(vec!["Af8=".into()], Some(BytesFormat::SEQ)).unwrap();
    assert_eq!(value, [1, 255]);

    // adapters are not affected
    let value: As<Vec<u8>, Base64Url> = deserialize_with(vec!["Af8".into()], dotted).unwrap();
    assert_eq!(*value, [1, 255]);
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Blob {
    plain: Vec<u8>,
    #[deser(as = BytesFallback<Dotted>)]
    array: [u8; 2],
    #[deser(as = BytesFallback<Dotted>)]
    encoded: Vec<u8>,
    #[deser(as = Dotted)]
    forced: Vec<u8>,
    #[deser(as = BytesFallback<IntSeq>)]
    seq: Vec<u8>,
    #[deser(as = Option<BytesFallback<Dotted>>)]
    optional: Option<Vec<u8>>,
    #[deser(as = Vec<BytesFallback<Dotted>>)]
    many: Vec<Vec<u8>>,
    #[deser(as = BTreeMap<BytesFallback<Dotted>, _>)]
    keys: BTreeMap<Vec<u8>, u32>,
    #[deser(as = BytesFallback<Dotted>)]
    cow: Cow<'static, [u8]>,
    #[deser(as = Option<Dotted>)]
    forced_optional: Option<[u8; 1]>,
    #[deser(as = Dotted)]
    forced_cow: Cow<'static, [u8]>,
}

fn blob() -> Blob {
    Blob {
        plain: vec![1],
        array: [2, 3],
        encoded: vec![4],
        forced: vec![5],
        seq: vec![6],
        optional: Some(vec![7]),
        many: vec![vec![8]],
        keys: [(vec![9], 10)].into_iter().collect(),
        cow: Cow::Borrowed(&[11]),
        forced_optional: Some([12]),
        forced_cow: Cow::Borrowed(&[13]),
    }
}

#[test]
fn test_adapters_serialize() {
    let dotted = Some(BytesFormat::encoded::<Dotted>());
    let seq = Some(BytesFormat::SEQ);
    let events = serialize(&blob());
    let expected = vec![
        (Event::map_start(), None),
        ("plain".into(), None),
        (bytes(&[1]), None),
        ("array".into(), None),
        (bytes(&[2, 3]), dotted),
        ("encoded".into(), None),
        (bytes(&[4]), dotted),
        ("forced".into(), None),
        // forced strings are strings for all formats
        ("5".into(), None),
        ("seq".into(), None),
        (bytes(&[6]), seq),
        ("optional".into(), None),
        (bytes(&[7]), dotted),
        ("many".into(), None),
        (Event::seq_start(), None),
        (bytes(&[8]), dotted),
        (Event::SeqEnd, None),
        ("keys".into(), None),
        (
            Event::MapStart(deser::ContainerShape::new().with_order(deser::Order::Sorted)),
            None,
        ),
        (bytes(&[9]), dotted),
        (10u64.into(), None),
        (Event::MapEnd, None),
        ("cow".into(), None),
        (bytes(&[11]), dotted),
        ("forced_optional".into(), None),
        ("12".into(), None),
        ("forced_cow".into(), None),
        ("13".into(), None),
        (Event::MapEnd, None),
    ];
    assert_eq!(events, expected);
}

#[test]
fn test_adapters_deserialize() {
    // from native bytes
    let events = serialize(&blob())
        .into_iter()
        .map(|(event, _)| event)
        .collect::<Vec<_>>();
    assert_eq!(deserialize::<Blob>(events).unwrap(), blob());

    // from strings
    let events = vec![
        Event::map_start(),
        "plain".into(),
        "AQ==".into(),
        "array".into(),
        "2.3".into(),
        "encoded".into(),
        "4".into(),
        "forced".into(),
        "5".into(),
        "seq".into(),
        Event::seq_start(),
        6u64.into(),
        Event::SeqEnd,
        "optional".into(),
        "7".into(),
        "many".into(),
        Event::seq_start(),
        "8".into(),
        Event::SeqEnd,
        "keys".into(),
        Event::map_start(),
        "9".into(),
        10u64.into(),
        Event::MapEnd,
        "cow".into(),
        "11".into(),
        "forced_optional".into(),
        "12".into(),
        "forced_cow".into(),
        "13".into(),
        Event::MapEnd,
    ];
    assert_eq!(deserialize::<Blob>(events).unwrap(), blob());

    let err = deserialize::<As<[u8; 2], Dotted>>(vec!["x".into()]).unwrap_err();
    assert_eq!(err.to_string(), "Unexpected: invalid byte");
    let err = deserialize::<As<[u8; 2], Dotted>>(vec!["1".into()]).unwrap_err();
    assert_eq!(err.kind(), ErrorKind::WrongLength);
    let err = deserialize::<As<Vec<u8>, Dotted>>(vec![1u64.into()]).unwrap_err();
    assert_eq!(
        err.to_string(),
        "Unexpected: unexpected unsigned integer, expected bytes or dotted string"
    );
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Custom {
    #[deser(as = BytesFallback<Dotted>)]
    hint: Vec<u8>,
    #[deser(as = Dotted)]
    forced: Vec<u8>,
}

#[test]
fn test_custom_encoding() {
    let value = Custom {
        hint: vec![1, 2],
        forced: vec![3, 4],
    };
    let events = serialize(&value);
    assert_eq!(events[2].1, Some(BytesFormat::encoded::<Dotted>()));
    assert_eq!(events[2].1.unwrap().name(), "dotted");
    assert_eq!(events[4].0, "3.4".into());

    let value: Custom = deserialize(vec![
        Event::map_start(),
        "hint".into(),
        "1.2".into(),
        "forced".into(),
        "3.4".into(),
        Event::MapEnd,
    ])
    .unwrap();
    assert_eq!(value.hint, [1, 2]);
    assert_eq!(value.forced, [3, 4]);
}