use cbor2::{Cbor, Value};
#[derive(Debug, PartialEq, Cbor)]
struct CoseKey {
#[cbor(key = 1)]
kty: u8,
#[cbor(key = 3)]
alg: i8,
#[cbor(key = -1)]
crv: u8,
#[cbor(key = -2)]
x: serde_bytes::ByteBuf,
note: Option<String>,
}
fn sample() -> CoseKey {
CoseKey {
kty: 2, alg: -7, crv: 1, x: serde_bytes::ByteBuf::from(vec![0x11, 0x22, 0x33, 0x44]),
note: None,
}
}
#[test]
fn cose_key_round_trip() {
let bytes = cbor2::to_vec(&sample()).unwrap();
assert_eq!(
hex::encode(&bytes),
"a5010203262001214411223344646e6f7465f6"
);
assert_eq!(cbor2::from_slice::<CoseKey>(&bytes).unwrap(), sample());
let value = Value::serialized(&sample()).unwrap();
assert_eq!(value.deserialized::<CoseKey>().unwrap(), sample());
let textual = cbor2::cbor!({
1 => 2,
"alg" => -7,
-1 => 1,
-2 => cbor2::Value::Bytes(vec![0x11, 0x22, 0x33, 0x44]),
"note" => null,
})
.unwrap();
assert_eq!(textual.deserialized::<CoseKey>().unwrap(), sample());
}
#[test]
fn json_just_works() {
let json = serde_json::to_string(&sample()).unwrap();
assert_eq!(
json,
r#"{"kty":2,"alg":-7,"crv":1,"x":[17,34,51,68],"note":null}"#
);
assert_eq!(serde_json::from_str::<CoseKey>(&json).unwrap(), sample());
}
#[derive(Debug, PartialEq, Cbor)]
struct LifetimeNames<'a, '__de> {
#[cbor(key = 1)]
value: u8,
#[serde(skip)]
marker: core::marker::PhantomData<(&'a (), &'__de ())>,
}
#[test]
fn derive_avoids_internal_lifetime_name_collisions() {
let value = LifetimeNames {
value: 7,
marker: core::marker::PhantomData,
};
let bytes = cbor2::to_vec(&value).unwrap();
assert_eq!(hex::encode(&bytes), "a10107");
}
#[derive(Debug, PartialEq, Cbor)]
#[cbor(tag = 123)]
struct ProtectedHeader {
#[cbor(key = 1)]
alg: i8,
#[cbor(key = 4)]
#[serde(with = "serde_bytes")]
kid: Vec<u8>,
}
fn header() -> ProtectedHeader {
ProtectedHeader {
alg: -7,
kid: b"kid".to_vec(),
}
}
#[test]
fn tagged_structs_wrap_and_require_their_tag() {
let bytes = cbor2::to_vec(&header()).unwrap();
assert_eq!(hex::encode(&bytes), "d87ba2012604436b6964");
assert_eq!(
cbor2::diagnostic(&bytes[..]).unwrap(),
"123({1: -7, 4: h'6b6964'})"
);
assert_eq!(
cbor2::from_slice::<ProtectedHeader>(&bytes).unwrap(),
header()
);
assert_eq!(cbor2::to_canonical_vec(&header()).unwrap(), bytes);
let untagged = hex::decode("a2012604436b6964").unwrap();
let msg = cbor2::from_slice::<ProtectedHeader>(&untagged)
.unwrap_err()
.to_string();
assert!(msg.contains("expected tag(123)"), "{msg}");
let wrong = hex::decode("d87ca2012604436b6964").unwrap(); let msg = cbor2::from_slice::<ProtectedHeader>(&wrong)
.unwrap_err()
.to_string();
assert!(msg.contains("expected tag(123)"), "{msg}");
let wrapped = hex::decode("d9d9f7d87ba2012604436b6964").unwrap(); assert_eq!(
cbor2::from_slice::<ProtectedHeader>(&wrapped).unwrap(),
header()
);
let value = Value::serialized(&header()).unwrap();
assert_eq!(
value,
Value::Tag(
123,
Box::new(cbor2::cbor!({ 1 => -7, 4 => Value::Bytes(b"kid".to_vec()) }).unwrap())
)
);
assert_eq!(value.deserialized::<ProtectedHeader>().unwrap(), header());
let untagged = cbor2::cbor!({ 1 => -7, 4 => Value::Bytes(b"kid".to_vec()) }).unwrap();
let msg = untagged
.deserialized::<ProtectedHeader>()
.unwrap_err()
.to_string();
assert!(msg.contains("expected tag(123)"), "{msg}");
let json = serde_json::to_string(&header()).unwrap();
assert_eq!(json, r#"{"alg":-7,"kid":[107,105,100]}"#);
assert_eq!(
serde_json::from_str::<ProtectedHeader>(&json).unwrap(),
header()
);
}
#[test]
fn tagged_tuple_structs_work_too() {
#[derive(Debug, PartialEq, Cbor)]
#[cbor(tag = 18)]
struct Sign1(
#[serde(with = "serde_bytes")] Vec<u8>,
u8,
#[serde(with = "serde_bytes")] Vec<u8>,
#[serde(with = "serde_bytes")] Vec<u8>,
);
let msg = Sign1(vec![0xa0], 0, vec![], vec![0xff]);
let bytes = cbor2::to_vec(&msg).unwrap();
assert_eq!(hex::encode(&bytes), "d28441a0004041ff");
assert_eq!(cbor2::from_slice::<Sign1>(&bytes).unwrap(), msg);
let msg2 = cbor2::from_slice::<Sign1>(&hex::decode("8441a0004041ff").unwrap());
assert!(msg2.unwrap_err().to_string().contains("expected tag(18)"));
}
#[test]
fn named_structs_can_use_cose_array_shape() {
#[derive(Debug, PartialEq, Cbor)]
#[cbor(tag = 18, array)]
struct Sign1 {
#[serde(with = "serde_bytes")]
protected: Vec<u8>,
unprotected: u8,
#[serde(with = "serde_bytes")]
payload: Vec<u8>,
#[serde(with = "serde_bytes")]
signature: Vec<u8>,
}
let msg = Sign1 {
protected: vec![0xa0],
unprotected: 0,
payload: vec![],
signature: vec![0xff],
};
assert_eq!(Sign1::KEYS, &[]);
assert_eq!(Sign1::TAG, Some(18));
const {
assert!(Sign1::ARRAY);
}
let bytes = cbor2::to_vec(&msg).unwrap();
assert_eq!(hex::encode(&bytes), "d28441a0004041ff");
assert_eq!(cbor2::from_slice::<Sign1>(&bytes).unwrap(), msg);
let json = serde_json::to_string(&msg).unwrap();
assert_eq!(
json,
r#"{"protected":[160],"unprotected":0,"payload":[],"signature":[255]}"#
);
assert_eq!(serde_json::from_str::<Sign1>(&json).unwrap(), msg);
}
#[test]
fn enums_and_generics_work_too() {
#[derive(Debug, PartialEq, Cbor)]
enum Message {
Signed {
#[cbor(key = 1)]
payload: u8,
label: bool,
},
Unit,
}
let bytes = cbor2::to_vec(&Message::Signed {
payload: 7,
label: true,
})
.unwrap();
assert_eq!(hex::encode(&bytes), "a1665369676e6564a20107656c6162656cf5");
assert_eq!(
cbor2::from_slice::<Message>(&bytes).unwrap(),
Message::Signed {
payload: 7,
label: true
}
);
assert_eq!(cbor2::to_vec(&Message::Unit).unwrap(), b"\x64Unit");
let json = serde_json::to_string(&Message::Signed {
payload: 7,
label: true,
})
.unwrap();
assert_eq!(json, r#"{"Signed":{"payload":7,"label":true}}"#);
#[derive(Debug, PartialEq, Cbor)]
#[cbor(tag = 7)]
struct Wrap<T> {
#[cbor(key = 1)]
inner: T,
}
let wrapped = Wrap { inner: 5u8 };
let bytes = cbor2::to_vec(&wrapped).unwrap();
assert_eq!(hex::encode(&bytes), "c7a10105"); assert_eq!(cbor2::from_slice::<Wrap<u8>>(&bytes).unwrap(), wrapped);
}
#[test]
fn serde_attributes_combine() {
#[derive(Debug, PartialEq, Cbor)]
struct Renamed {
#[cbor(key = 3)]
#[serde(rename = "alg", alias = "algorithm")]
algorithm: i8,
#[serde(default)]
note: String,
}
let value = Renamed {
algorithm: -7,
note: String::new(),
};
let bytes = cbor2::to_vec(&value).unwrap();
assert_eq!(hex::encode(&bytes), "a20326646e6f746560");
assert_eq!(cbor2::from_slice::<Renamed>(&bytes).unwrap(), value);
assert_eq!(
serde_json::to_string(&value).unwrap(),
r#"{"alg":-7,"note":""}"#
);
let parsed: Renamed = serde_json::from_str(r#"{"algorithm":-7}"#).unwrap();
assert_eq!(parsed, value);
}
#[test]
fn full_key_range() {
#[derive(Debug, PartialEq, Cbor)]
#[cbor(tag = 18446744073709551615)]
struct Edges {
#[cbor(key = 0)]
zero: u8,
#[cbor(key = 18446744073709551615)]
hi: u8,
#[cbor(key = -18446744073709551616)]
lo: u8,
}
let edges = Edges {
zero: 0,
hi: 1,
lo: 2,
};
let bytes = cbor2::to_vec(&edges).unwrap();
assert_eq!(
hex::encode(&bytes),
"dbffffffffffffffffa300001bffffffffffffffff013bffffffffffffffff02"
);
assert_eq!(cbor2::from_slice::<Edges>(&bytes).unwrap(), edges);
}
#[test]
fn derive_exposes_keys_and_tag() {
assert_eq!(
CoseKey::KEYS,
&[("kty", 1), ("alg", 3), ("crv", -1), ("x", -2)]
);
assert_eq!(CoseKey::TAG, None);
assert_eq!(ProtectedHeader::KEYS, &[("alg", 1), ("kid", 4)]);
assert_eq!(ProtectedHeader::TAG, Some(123));
let keys = sample().keys();
assert_eq!(keys.len(), 4);
assert_eq!(keys["kty"], 1);
assert_eq!(keys["x"], -2);
assert!(!keys.contains_key("note"));
#[derive(Cbor)]
struct Plain {
#[allow(dead_code)]
a: u8,
}
assert_eq!(Plain::KEYS, &[]);
assert_eq!(Plain::TAG, None);
assert!(Plain { a: 0 }.keys().is_empty());
}