wolfcose 0.1.0

Safe Rust API for wolfSSL wolfCOSE.
#![allow(missing_docs)]

use wolfcose::{
    from_slice, to_vec, ByteBuf, CborDeserialize, CborDeserializer, CborSerialize, CborSerializer,
};

#[derive(Debug, PartialEq, CborSerialize, CborDeserialize)]
struct Device {
    id: u32,
    #[cbor(rename = "kid")]
    key_id: String,
    #[cbor(default)]
    note: String,
    #[cbor(skip)]
    cache: Vec<u8>,
}

#[derive(Debug, PartialEq, CborSerialize, CborDeserialize)]
struct Pair(u8, String, ByteBuf);

#[derive(Debug, PartialEq, CborSerialize, CborDeserialize)]
struct Marker;

#[derive(Debug, PartialEq, CborSerialize, CborDeserialize)]
struct Wrapper<T> {
    value: T,
    #[cbor(default)]
    fallback: Option<T>,
}

struct DeviceWithoutOptionalFields;

impl CborSerialize for DeviceWithoutOptionalFields {
    fn serialize(&self, serializer: &mut CborSerializer<'_>) -> wolfcose::Result<()> {
        serializer.map(3)?;
        "id".serialize(serializer)?;
        9u32.serialize(serializer)?;
        "kid".serialize(serializer)?;
        "alpha".serialize(serializer)?;
        "extra".serialize(serializer)?;
        42u8.serialize(serializer)?;
        Ok(())
    }
}

struct DeviceMissingRequiredField;

impl CborSerialize for DeviceMissingRequiredField {
    fn serialize(&self, serializer: &mut CborSerializer<'_>) -> wolfcose::Result<()> {
        serializer.map(1)?;
        "kid".serialize(serializer)?;
        "alpha".serialize(serializer)?;
        Ok(())
    }
}

#[test]
fn derived_named_struct_round_trips_with_field_attributes() {
    let device = Device {
        id: 7,
        key_id: "sensor-a".to_owned(),
        note: "lab".to_owned(),
        cache: vec![1, 2, 3],
    };

    let encoded = to_vec(&device).unwrap();
    let decoded: Device = from_slice(&encoded).unwrap();

    assert_eq!(
        decoded,
        Device {
            cache: Vec::new(),
            ..device
        }
    );
}

#[test]
fn derived_named_struct_defaults_and_skips_unknown_fields() {
    let encoded = to_vec(&DeviceWithoutOptionalFields).unwrap();
    let decoded: Device = from_slice(&encoded).unwrap();

    assert_eq!(
        decoded,
        Device {
            id: 9,
            key_id: "alpha".to_owned(),
            note: String::new(),
            cache: Vec::new(),
        }
    );
}

#[test]
fn derived_named_struct_rejects_missing_required_fields() {
    let encoded = to_vec(&DeviceMissingRequiredField).unwrap();
    assert!(from_slice::<Device>(&encoded).is_err());
}

#[test]
fn derived_tuple_and_unit_structs_round_trip() {
    let pair = Pair(1, "x".to_owned(), ByteBuf(vec![0xaa, 0xbb]));
    assert_eq!(from_slice::<Pair>(&to_vec(&pair).unwrap()).unwrap(), pair);

    let marker = Marker;
    assert_eq!(
        from_slice::<Marker>(&to_vec(&marker).unwrap()).unwrap(),
        marker
    );
}

#[test]
fn derived_generic_struct_round_trips() {
    let wrapper = Wrapper {
        value: 123u64,
        fallback: Some(456),
    };

    assert_eq!(
        from_slice::<Wrapper<u64>>(&to_vec(&wrapper).unwrap()).unwrap(),
        wrapper
    );
}

#[test]
fn derived_tuple_struct_rejects_wrong_array_len() {
    let mut out = [0u8; 16];
    let mut serializer = CborSerializer::new(&mut out);
    serializer.array(2).unwrap();
    1u8.serialize(&mut serializer).unwrap();
    "x".serialize(&mut serializer).unwrap();
    let encoded = serializer.as_written();

    let mut deserializer = CborDeserializer::new(encoded);
    assert!(Pair::deserialize(&mut deserializer).is_err());
}