use cbor2::tag::{AllowAny, AllowExact, RequireAny, RequireExact};
use cbor2::Value;
#[test]
fn require_exact() {
let datetime = RequireExact::<String, 0>("2013-03-21T20:04:00Z".into());
let bytes = cbor2::to_vec(&datetime).unwrap();
assert_eq!(
hex::encode(&bytes),
"c074323031332d30332d32315432303a30343a30305a"
);
let back: RequireExact<String, 0> = cbor2::from_slice(&bytes).unwrap();
assert_eq!(back, datetime);
assert!(cbor2::from_slice::<RequireExact<String, 1>>(&bytes).is_err());
let untagged = cbor2::to_vec(&"2013-03-21T20:04:00Z").unwrap();
assert!(cbor2::from_slice::<RequireExact<String, 0>>(&untagged).is_err());
}
#[test]
fn allow_exact() {
let bytes = cbor2::to_vec(&AllowExact::<u64, 1>(1363896240)).unwrap();
assert_eq!(hex::encode(&bytes), "c11a514b67b0");
let tagged: AllowExact<u64, 1> = cbor2::from_slice(&bytes).unwrap();
assert_eq!(tagged.0, 1363896240);
let untagged_bytes = cbor2::to_vec(&1363896240u64).unwrap();
let untagged: AllowExact<u64, 1> = cbor2::from_slice(&untagged_bytes).unwrap();
assert_eq!(untagged.0, 1363896240);
assert!(cbor2::from_slice::<AllowExact<u64, 7>>(&bytes).is_err());
}
#[test]
fn require_any() {
let bytes = cbor2::to_vec(&RequireAny(32, String::from("https://example.com"))).unwrap();
let back: RequireAny<String> = cbor2::from_slice(&bytes).unwrap();
assert_eq!(back.0, 32);
assert_eq!(back.1, "https://example.com");
let untagged = cbor2::to_vec(&"https://example.com").unwrap();
assert!(cbor2::from_slice::<RequireAny<String>>(&untagged).is_err());
}
#[test]
fn allow_any() {
let tagged = hex::decode("c11a514b67b0").unwrap();
let back: AllowAny<u64> = cbor2::from_slice(&tagged).unwrap();
assert_eq!(back, AllowAny(Some(1), 1363896240));
let untagged = cbor2::to_vec(&1363896240u64).unwrap();
let back: AllowAny<u64> = cbor2::from_slice(&untagged).unwrap();
assert_eq!(back, AllowAny(None, 1363896240));
let bytes = cbor2::to_vec(&AllowAny(Some(1), 1363896240u64)).unwrap();
assert_eq!(hex::encode(&bytes), "c11a514b67b0");
let bytes = cbor2::to_vec(&AllowAny(None, 1363896240u64)).unwrap();
assert_eq!(hex::encode(&bytes), "1a514b67b0");
}
#[test]
fn value_tags() {
let value = Value::Tag(262, Box::new(Value::from("x")));
let bytes = cbor2::to_vec(&value).unwrap();
assert_eq!(hex::encode(&bytes), "d901066178");
let back: Value = cbor2::from_slice(&bytes).unwrap();
assert_eq!(back, value);
let nested = Value::Tag(1, Box::new(Value::Tag(0, Box::new(Value::from("x")))));
let back: Value = cbor2::from_slice(&cbor2::to_vec(&nested).unwrap()).unwrap();
assert_eq!(back, nested);
let wrapped: RequireAny<String> = value.deserialized().unwrap();
assert_eq!(wrapped, RequireAny(262, "x".into()));
let again = Value::serialized(&wrapped).unwrap();
assert_eq!(again, value);
}
#[test]
fn oversized_bignums_stay_tagged() {
let mut payload = vec![1u8];
payload.extend_from_slice(&[0u8; 16]);
let value = Value::Tag(2, Box::new(Value::Bytes(payload)));
let bytes = cbor2::to_vec(&value).unwrap();
let back: Value = cbor2::from_slice(&bytes).unwrap();
assert_eq!(back, value);
let small = Value::Tag(2, Box::new(Value::Bytes(vec![42])));
let bytes = cbor2::to_vec(&small).unwrap();
let back: Value = cbor2::from_slice(&bytes).unwrap();
assert_eq!(back, Value::from(42));
}
mod synthetic {
use serde::de::value::{
BoolDeserializer, Error as DeError, SeqDeserializer, StrDeserializer, U64Deserializer,
};
use serde::de::{self, IntoDeserializer};
use serde::forward_to_deserialize_any;
use cbor2::tag::AllowAny;
pub enum VariantId {
Index(u64),
Name(&'static str),
NotAnIdentifier,
}
pub struct EnumDe {
pub variant: VariantId,
pub items: Vec<u64>,
}
impl<'de> de::Deserializer<'de> for EnumDe {
type Error = DeError;
fn deserialize_any<V: de::Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("not an enum"))
}
fn deserialize_enum<V: de::Visitor<'de>>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128
bool f32 f64 char str string bytes byte_buf
seq map struct tuple tuple_struct identifier ignored_any
option unit unit_struct newtype_struct
}
}
impl<'de> de::EnumAccess<'de> for EnumDe {
type Error = DeError;
type Variant = Self;
fn variant_seed<V: de::DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
let value = match self.variant {
VariantId::Index(x) => seed.deserialize(U64Deserializer::new(x))?,
VariantId::Name(x) => seed.deserialize(StrDeserializer::new(x))?,
VariantId::NotAnIdentifier => seed.deserialize(BoolDeserializer::new(true))?,
};
Ok((value, self))
}
}
impl<'de> de::VariantAccess<'de> for EnumDe {
type Error = DeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
self,
seed: U,
) -> Result<U::Value, Self::Error> {
seed.deserialize(self.items[0].into_deserializer())
}
fn tuple_variant<V: de::Visitor<'de>>(
self,
_: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_seq(SeqDeserializer::new(self.items.into_iter()))
}
fn struct_variant<V: de::Visitor<'de>>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("no structs here"))
}
}
fn allow_any(variant: VariantId, items: Vec<u64>) -> Result<AllowAny<u64>, DeError> {
use serde::Deserialize;
AllowAny::deserialize(EnumDe { variant, items })
}
#[test]
fn variant_indices_select_the_form() {
assert_eq!(
allow_any(VariantId::Index(0), vec![5]).unwrap(),
AllowAny(None, 5)
);
assert_eq!(
allow_any(VariantId::Index(1), vec![9, 5]).unwrap(),
AllowAny(Some(9), 5)
);
let msg = allow_any(VariantId::Index(2), vec![])
.unwrap_err()
.to_string();
assert!(msg.contains("variant index 0 or 1"), "{msg}");
}
#[test]
fn variant_names_are_validated() {
assert_eq!(
allow_any(VariantId::Name("@@UNTAGGED@@"), vec![5]).unwrap(),
AllowAny(None, 5)
);
let msg = allow_any(VariantId::Name("bogus"), vec![])
.unwrap_err()
.to_string();
assert!(msg.contains("unknown variant"), "{msg}");
let msg = allow_any(VariantId::NotAnIdentifier, vec![])
.unwrap_err()
.to_string();
assert!(msg.contains("a tag variant identifier"), "{msg}");
}
#[test]
fn tagged_payload_lengths_are_validated() {
let msg = allow_any(VariantId::Index(1), vec![])
.unwrap_err()
.to_string();
assert!(msg.contains("invalid length 0"), "{msg}");
assert!(msg.contains("a tag number and a value"), "{msg}");
let msg = allow_any(VariantId::Index(1), vec![9])
.unwrap_err()
.to_string();
assert!(msg.contains("invalid length 1"), "{msg}");
}
#[test]
fn tagged_payloads_with_wrong_types_are_rejected() {
struct BadTagItems;
impl<'de> de::Deserializer<'de> for BadTagItems {
type Error = DeError;
fn deserialize_any<V: de::Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("not an enum"))
}
fn deserialize_enum<V: de::Visitor<'de>>(
self,
_: &'static str,
_: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128
bool f32 f64 char str string bytes byte_buf
seq map struct tuple tuple_struct identifier ignored_any
option unit unit_struct newtype_struct
}
}
impl<'de> de::EnumAccess<'de> for BadTagItems {
type Error = DeError;
type Variant = Self;
fn variant_seed<V: de::DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
let value = seed.deserialize(StrDeserializer::new("@@TAGGED@@"))?;
Ok((value, self))
}
}
impl<'de> de::VariantAccess<'de> for BadTagItems {
type Error = DeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
self,
seed: U,
) -> Result<U::Value, Self::Error> {
seed.deserialize(StrDeserializer::new("x"))
}
fn tuple_variant<V: de::Visitor<'de>>(
self,
_: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_seq(SeqDeserializer::new(["oops"].into_iter()))
}
fn struct_variant<V: de::Visitor<'de>>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("no structs here"))
}
}
use serde::Deserialize;
assert!(AllowAny::<u64>::deserialize(BadTagItems).is_err());
}
#[test]
fn non_enum_input_is_rejected() {
use serde::Deserialize;
let err = AllowAny::<u64>::deserialize(BoolDeserializer::<DeError>::new(true));
let msg = err.unwrap_err().to_string();
assert!(msg.contains("a possibly tagged value"), "{msg}");
}
}
mod foreign_value {
use serde::de::value::{Error as DeError, SeqDeserializer, StrDeserializer};
use serde::de::{self, Expected};
use serde::forward_to_deserialize_any;
use serde::Deserialize;
use cbor2::Value;
pub struct EnumDe {
pub variant: &'static str,
pub items: Vec<u64>,
}
impl<'de> de::Deserializer<'de> for EnumDe {
type Error = DeError;
fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128
bool f32 f64 char str string bytes byte_buf
seq map struct tuple tuple_struct identifier ignored_any
option unit unit_struct newtype_struct enum
}
}
impl<'de> de::EnumAccess<'de> for EnumDe {
type Error = DeError;
type Variant = Self;
fn variant_seed<V: de::DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
let value = seed.deserialize(StrDeserializer::new(self.variant))?;
Ok((value, self))
}
}
impl<'de> de::VariantAccess<'de> for EnumDe {
type Error = DeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
self,
seed: U,
) -> Result<U::Value, Self::Error> {
seed.deserialize(StrDeserializer::new("payload"))
}
fn tuple_variant<V: de::Visitor<'de>>(
self,
_: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
assert!(!format!("{}", DisplayExpected(&visitor)).is_empty());
visitor.visit_seq(SeqDeserializer::new(self.items.into_iter()))
}
fn struct_variant<V: de::Visitor<'de>>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("no structs here"))
}
}
struct DisplayExpected<'a, T>(&'a T);
impl<T: Expected> core::fmt::Display for DisplayExpected<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
#[test]
fn variant_identifier_seed_failures_propagate() {
struct BadVariantDe;
impl<'de> de::Deserializer<'de> for BadVariantDe {
type Error = DeError;
fn deserialize_any<V: de::Visitor<'de>>(
self,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128
bool f32 f64 char str string bytes byte_buf
seq map struct tuple tuple_struct identifier ignored_any
option unit unit_struct newtype_struct enum
}
}
impl<'de> de::EnumAccess<'de> for BadVariantDe {
type Error = DeError;
type Variant = Self;
fn variant_seed<V: de::DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
let value = seed.deserialize(serde::de::value::BoolDeserializer::new(true))?;
Ok((value, self))
}
}
impl<'de> de::VariantAccess<'de> for BadVariantDe {
type Error = DeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
self,
_: U,
) -> Result<U::Value, Self::Error> {
Err(de::Error::custom("unused"))
}
fn tuple_variant<V: de::Visitor<'de>>(
self,
_: usize,
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("unused"))
}
fn struct_variant<V: de::Visitor<'de>>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("unused"))
}
}
use serde::Deserialize;
assert!(Value::deserialize(BadVariantDe).is_err());
}
#[test]
fn value_accepts_the_tagged_variant() {
let value = Value::deserialize(EnumDe {
variant: "@@TAGGED@@",
items: vec![9, 5],
})
.unwrap();
assert_eq!(value, Value::Tag(9, Box::new(Value::from(5))));
}
#[test]
fn value_rejects_bad_tag_payload_types() {
let err = Value::deserialize(BadItemsDe).unwrap_err().to_string();
assert!(err.contains("invalid type"), "{err}");
}
pub struct BadItemsDe;
impl<'de> de::Deserializer<'de> for BadItemsDe {
type Error = DeError;
fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
visitor.visit_enum(self)
}
forward_to_deserialize_any! {
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128
bool f32 f64 char str string bytes byte_buf
seq map struct tuple tuple_struct identifier ignored_any
option unit unit_struct newtype_struct enum
}
}
impl<'de> de::EnumAccess<'de> for BadItemsDe {
type Error = DeError;
type Variant = Self;
fn variant_seed<V: de::DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
let value = seed.deserialize(StrDeserializer::new("@@TAGGED@@"))?;
Ok((value, self))
}
}
impl<'de> de::VariantAccess<'de> for BadItemsDe {
type Error = DeError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
self,
seed: U,
) -> Result<U::Value, Self::Error> {
seed.deserialize(StrDeserializer::new("x"))
}
fn tuple_variant<V: de::Visitor<'de>>(
self,
_: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
visitor.visit_seq(SeqDeserializer::new(["oops"].into_iter()))
}
fn struct_variant<V: de::Visitor<'de>>(
self,
_: &'static [&'static str],
_: V,
) -> Result<V::Value, Self::Error> {
Err(de::Error::custom("no structs here"))
}
}
#[test]
fn value_rejects_other_variants_and_short_payloads() {
let msg = Value::deserialize(EnumDe {
variant: "@@UNTAGGED@@",
items: vec![],
})
.unwrap_err()
.to_string();
assert!(msg.contains("expected tag"), "{msg}");
let msg = Value::deserialize(EnumDe {
variant: "@@TAGGED@@",
items: vec![],
})
.unwrap_err()
.to_string();
assert!(msg.contains("expected tag"), "{msg}");
let msg = Value::deserialize(EnumDe {
variant: "@@TAGGED@@",
items: vec![9],
})
.unwrap_err()
.to_string();
assert!(msg.contains("expected value"), "{msg}");
}
}
#[test]
fn wrappers_reject_garbage_input() {
use cbor2::tag::{AllowAny, AllowExact, RequireAny, RequireExact};
let untagged = [0x01u8];
assert_eq!(
cbor2::from_slice::<AllowAny<u8>>(&untagged).unwrap(),
AllowAny(None, 1)
);
assert_eq!(
cbor2::from_slice::<AllowExact<u8, 1>>(&untagged).unwrap(),
AllowExact(1)
);
assert!(cbor2::from_slice::<RequireAny<u8>>(&untagged).is_err());
assert!(cbor2::from_slice::<RequireExact<u8, 1>>(&untagged).is_err());
let tagged_bool = [0xc1, 0xf5];
assert!(cbor2::from_slice::<AllowAny<u8>>(&tagged_bool).is_err());
assert!(cbor2::from_slice::<RequireExact<u8, 1>>(&tagged_bool).is_err());
}
#[test]
fn wrapper_payload_failures_propagate() {
use cbor2::tag::{AllowAny, AllowExact, RequireAny, RequireExact};
let not_a_u8 = [0xf5u8]; assert!(cbor2::from_slice::<AllowAny<u8>>(¬_a_u8).is_err());
assert!(cbor2::from_slice::<AllowExact<u8, 1>>(¬_a_u8).is_err());
assert!(cbor2::from_slice::<RequireAny<u8>>(¬_a_u8).is_err());
assert!(cbor2::from_slice::<RequireExact<u8, 1>>(¬_a_u8).is_err());
}
struct WrapperAsTagNumber;
impl serde::Serialize for WrapperAsTagNumber {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeTupleVariant;
let mut acc = serializer.serialize_tuple_variant("@@TAG@@", 1, "@@TAGGED@@", 2)?;
acc.serialize_field(&cbor2::tag::AllowAny(Some(1), 0u8))?;
acc.serialize_field(&0u8)?;
acc.end()
}
}
#[test]
fn tag_protocol_rejects_wrapper_tag_numbers() {
assert!(cbor2::to_vec(&WrapperAsTagNumber).is_err());
assert!(Value::serialized(&WrapperAsTagNumber).is_err());
}