From 5bd0c85ce0809593ea123a0af3139d41033176d5 Mon Sep 17 00:00:00 2001 From: Emanuel Pilz Date: Tue, 22 Oct 2024 15:00:23 +0200 Subject: [PATCH] feat: `Value` representation of `Simple` values Co-authored-by: Hendrik Wolff Co-authored-by: Theodor Straube Co-authored-by: moehr1z Signed-off-by: Emanuel Pilz --- ciborium/src/de/mod.rs | 21 +- ciborium/src/lib.rs | 1 + ciborium/src/ser/mod.rs | 10 +- ciborium/src/simple.rs | 403 ++++++++++++++++++++++++++++++++ ciborium/src/value/canonical.rs | 1 + ciborium/src/value/de.rs | 37 ++- ciborium/src/value/mod.rs | 19 +- ciborium/src/value/ser.rs | 17 +- ciborium/tests/macro.rs | 96 ++++---- ciborium/tests/simple.rs | 58 +++++ ciborium/tests/value.rs | 80 +++++++ 11 files changed, 667 insertions(+), 76 deletions(-) create mode 100644 ciborium/src/simple.rs create mode 100644 ciborium/tests/simple.rs create mode 100644 ciborium/tests/value.rs diff --git a/ciborium/src/de/mod.rs b/ciborium/src/de/mod.rs index 18742e4..680a04e 100644 --- a/ciborium/src/de/mod.rs +++ b/ciborium/src/de/mod.rs @@ -213,8 +213,11 @@ where Header::Simple(simple::FALSE) => self.deserialize_bool(visitor), Header::Simple(simple::TRUE) => self.deserialize_bool(visitor), Header::Simple(simple::NULL) => self.deserialize_option(visitor), - Header::Simple(simple::UNDEFINED) => self.deserialize_option(visitor), - h @ Header::Simple(..) => Err(h.expected("known simple value")), + Header::Simple(_) => self.recurse(|me| { + let mut simple_de = + crate::simple::SimpleDeserializer::<_, Self>::new(&mut me.decoder); + visitor.visit_newtype_struct(&mut simple_de) + }), h @ Header::Break => Err(h.expected("non-break")), } @@ -222,16 +225,10 @@ where #[inline] fn deserialize_bool>(self, visitor: V) -> Result { - loop { - let offset = self.decoder.offset(); - - return match self.decoder.pull()? { - Header::Tag(..) => continue, - Header::Simple(simple::FALSE) => visitor.visit_bool(false), - Header::Simple(simple::TRUE) => visitor.visit_bool(true), - _ => Err(Error::semantic(offset, "expected bool")), - }; - } + self.recurse(|me| { + let mut simple_de = crate::simple::SimpleDeserializer::<_, Self>::new(&mut me.decoder); + simple_de.deserialize_bool(visitor) + }) } #[inline] diff --git a/ciborium/src/lib.rs b/ciborium/src/lib.rs index f143943..ed5ca47 100644 --- a/ciborium/src/lib.rs +++ b/ciborium/src/lib.rs @@ -94,6 +94,7 @@ extern crate alloc; pub mod de; pub mod ser; +pub mod simple; pub mod tag; pub mod value; diff --git a/ciborium/src/ser/mod.rs b/ciborium/src/ser/mod.rs index 03dd1da..eeeb1fe 100644 --- a/ciborium/src/ser/mod.rs +++ b/ciborium/src/ser/mod.rs @@ -200,10 +200,16 @@ where #[inline] fn serialize_newtype_struct( self, - _name: &'static str, + name: &'static str, value: &U, ) -> Result<(), Self::Error> { - value.serialize(self) + match name { + "@@SIMPLE@@" => match value.serialize(crate::simple::Serializer) { + Ok(x) => Ok(self.0.push(Header::Simple(x))?), + _ => Err(Error::Value("expected simple value".into())), + }, + _ => value.serialize(self), + } } #[inline] diff --git a/ciborium/src/simple.rs b/ciborium/src/simple.rs new file mode 100644 index 0000000..4abafc4 --- /dev/null +++ b/ciborium/src/simple.rs @@ -0,0 +1,403 @@ +//! Contains helper types for dealing with CBOR simple values + +use ciborium_ll::{simple, Decoder, Header}; +use serde::{de, forward_to_deserialize_any, ser}; + +pub(crate) struct SimpleDeserializer<'d, R, D> { + parent: core::marker::PhantomData, + decoder: &'d mut Decoder, +} + +impl<'d, R, D> SimpleDeserializer<'d, R, D> { + pub fn new(decoder: &'d mut Decoder) -> Self { + Self { + parent: core::marker::PhantomData, + decoder, + } + } +} + +impl<'de, 'd, D: de::Deserializer<'de>, R: ciborium_io::Read> de::Deserializer<'de> + for &mut SimpleDeserializer<'d, R, D> +where + R::Error: core::fmt::Debug, +{ + type Error = crate::de::Error; + + #[inline] + fn deserialize_any>(self, visitor: V) -> Result { + let offset = self.decoder.offset(); + + let Header::Simple(simple) = self.decoder.pull()? else { + return Err(crate::de::Error::semantic(offset, "expected simple")); + }; + visitor.visit_u8(simple) + } + + #[inline] + fn deserialize_bool>(self, visitor: V) -> Result { + loop { + let offset = self.decoder.offset(); + + return match self.decoder.pull()? { + Header::Tag(..) => continue, + Header::Simple(simple::FALSE) => visitor.visit_bool(false), + Header::Simple(simple::TRUE) => visitor.visit_bool(true), + _ => Err(crate::de::Error::semantic(offset, "expected bool")), + }; + } + } + + forward_to_deserialize_any! { + i8 i16 i32 i64 i128 + u8 u16 u32 u64 u128 + f32 f64 + char str string + bytes byte_buf + seq map + struct tuple tuple_struct + identifier ignored_any + option unit unit_struct newtype_struct enum + } +} + +#[derive(Debug)] +pub(crate) struct Error; + +impl core::fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ser::StdError for Error {} + +impl ser::Error for Error { + fn custom(_msg: U) -> Self { + Error + } +} + +pub(crate) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = u8; + type Error = Error; + + type SerializeSeq = Self; + type SerializeTuple = Self; + type SerializeTupleStruct = Self; + type SerializeTupleVariant = Self; + type SerializeMap = Self; + type SerializeStruct = Self; + type SerializeStructVariant = Self; + + #[inline] + fn serialize_bool(self, _: bool) -> Result { + Err(Error) + } + + #[inline] + fn serialize_i8(self, _: i8) -> Result { + Err(Error) + } + + #[inline] + fn serialize_i16(self, _: i16) -> Result { + Err(Error) + } + + #[inline] + fn serialize_i32(self, _: i32) -> Result { + Err(Error) + } + + #[inline] + fn serialize_i64(self, _: i64) -> Result { + Err(Error) + } + + #[inline] + fn serialize_i128(self, _: i128) -> Result { + Err(Error) + } + + #[inline] + fn serialize_u8(self, v: u8) -> Result { + Ok(v) + } + + #[inline] + fn serialize_u16(self, _: u16) -> Result { + Err(Error) + } + + #[inline] + fn serialize_u32(self, _: u32) -> Result { + Err(Error) + } + + #[inline] + fn serialize_u64(self, _: u64) -> Result { + Err(Error) + } + + #[inline] + fn serialize_u128(self, _: u128) -> Result { + Err(Error) + } + + #[inline] + fn serialize_f32(self, _: f32) -> Result { + Err(Error) + } + + #[inline] + fn serialize_f64(self, _: f64) -> Result { + Err(Error) + } + + #[inline] + fn serialize_char(self, _: char) -> Result { + Err(Error) + } + + #[inline] + fn serialize_str(self, _: &str) -> Result { + Err(Error) + } + + #[inline] + fn serialize_bytes(self, _: &[u8]) -> Result { + Err(Error) + } + + #[inline] + fn serialize_none(self) -> Result { + Err(Error) + } + + #[inline] + fn serialize_some(self, _: &U) -> Result { + Err(Error) + } + + #[inline] + fn serialize_unit(self) -> Result { + Err(Error) + } + + #[inline] + fn serialize_unit_struct(self, _name: &'static str) -> Result { + Err(Error) + } + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + _value: &U, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_newtype_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _value: &U, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_seq(self, _length: Option) -> Result { + Err(Error) + } + + #[inline] + fn serialize_tuple(self, _length: usize) -> Result { + Err(Error) + } + + #[inline] + fn serialize_tuple_struct( + self, + _name: &'static str, + _length: usize, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_tuple_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _length: usize, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_map(self, _length: Option) -> Result { + Err(Error) + } + + #[inline] + fn serialize_struct( + self, + _name: &'static str, + _length: usize, + ) -> Result { + Err(Error) + } + + #[inline] + fn serialize_struct_variant( + self, + _name: &'static str, + _index: u32, + _variant: &'static str, + _length: usize, + ) -> Result { + Err(Error) + } + + #[inline] + fn is_human_readable(&self) -> bool { + false + } +} + +impl ser::SerializeSeq for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_element(&mut self, _value: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeTuple for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_element(&mut self, _value: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeTupleStruct for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_field(&mut self, _value: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeTupleVariant for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_field(&mut self, _value: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeMap for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_key(&mut self, _key: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn serialize_value(&mut self, _value: &U) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeStruct for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_field( + &mut self, + _key: &'static str, + _value: &U, + ) -> Result<(), Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} + +impl ser::SerializeStructVariant for Serializer { + type Ok = u8; + type Error = Error; + + #[inline] + fn serialize_field( + &mut self, + _key: &'static str, + _value: &U, + ) -> Result<(), Self::Error> { + Err(Error) + } + + #[inline] + fn end(self) -> Result { + Err(Error) + } +} diff --git a/ciborium/src/value/canonical.rs b/ciborium/src/value/canonical.rs index 072e1cf..823d96a 100644 --- a/ciborium/src/value/canonical.rs +++ b/ciborium/src/value/canonical.rs @@ -55,6 +55,7 @@ pub fn cmp_value(v1: &Value, v2: &Value) -> Ordering { }, Some(x) => x, }, + (Simple(s), Simple(o)) => s.cmp(o), (_, _) => serialized_canonical_cmp(v1, v2), } } diff --git a/ciborium/src/value/de.rs b/ciborium/src/value/de.rs index f58a017..701f6cb 100644 --- a/ciborium/src/value/de.rs +++ b/ciborium/src/value/de.rs @@ -7,7 +7,10 @@ use super::{Error, Integer, Value}; use alloc::{boxed::Box, string::String, vec::Vec}; use core::iter::Peekable; -use ciborium_ll::tag; +use ciborium_ll::{ + simple::{FALSE, NULL, TRUE, UNDEFINED}, + tag, +}; use serde::de::{self, Deserializer as _}; impl<'a> From for de::Unexpected<'a> { @@ -28,14 +31,17 @@ impl<'a> From<&'a Value> for de::Unexpected<'a> { fn from(value: &'a Value) -> Self { match value { Value::Bool(x) => Self::Bool(*x), + Value::Simple(FALSE) => Self::Bool(false), + Value::Simple(TRUE) => Self::Bool(true), Value::Integer(x) => Self::from(*x), Value::Float(x) => Self::Float(*x), Value::Bytes(x) => Self::Bytes(x), Value::Text(x) => Self::Str(x), Value::Array(..) => Self::Seq, Value::Map(..) => Self::Map, - Value::Null => Self::Other("null"), + Value::Null | Value::Simple(NULL) => Self::Other("null"), Value::Tag(..) => Self::Other("tag"), + Value::Simple(_) => Self::Other("simple"), } } } @@ -110,7 +116,21 @@ impl<'de> serde::de::Visitor<'de> for Visitor { self, deserializer: D, ) -> Result { - deserializer.deserialize_any(self) + struct Inner; + + impl<'de> serde::de::Visitor<'de> for Inner { + type Value = Value; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a simple value") + } + + fn visit_u8(self, v: u8) -> Result { + Ok(Value::Simple(v)) + } + } + + deserializer.deserialize_any(Inner) } #[inline] @@ -234,7 +254,9 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())), Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())), Value::Bool(x) => visitor.visit_bool(*x), - Value::Null => visitor.visit_none(), + Value::Simple(FALSE) => visitor.visit_bool(false), + Value::Simple(TRUE) => visitor.visit_bool(true), + Value::Null | Value::Simple(NULL | UNDEFINED) => visitor.visit_none(), Value::Tag(t, v) => { let parent: Deserializer<&Value> = Deserializer(v); @@ -253,6 +275,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { } Value::Float(x) => visitor.visit_f64(*x), + Value::Simple(_) => visitor.visit_newtype_struct(self), } } @@ -446,7 +469,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { #[inline] fn deserialize_option>(self, visitor: V) -> Result { match self.0 { - Value::Null => visitor.visit_none(), + Value::Null | Value::Simple(NULL) => visitor.visit_none(), x => visitor.visit_some(Self(x)), } } @@ -454,7 +477,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { #[inline] fn deserialize_unit>(self, visitor: V) -> Result { match self.0 { - Value::Null => visitor.visit_unit(), + Value::Null | Value::Simple(NULL) => visitor.visit_unit(), _ => Err(de::Error::invalid_type(self.0.into(), &"null")), } } @@ -605,7 +628,7 @@ impl<'a, 'de> de::VariantAccess<'de> for Deserializer<&'a Value> { #[inline] fn unit_variant(self) -> Result<(), Self::Error> { match self.0 { - Value::Null => Ok(()), + Value::Null | Value::Simple(NULL) => Ok(()), _ => Err(de::Error::invalid_type(self.0.into(), &"unit")), } } diff --git a/ciborium/src/value/mod.rs b/ciborium/src/value/mod.rs index 7233026..8f742e9 100644 --- a/ciborium/src/value/mod.rs +++ b/ciborium/src/value/mod.rs @@ -10,6 +10,7 @@ mod error; mod ser; pub use canonical::CanonicalValue; +use ciborium_ll::simple::{FALSE, NULL, TRUE, UNDEFINED}; pub use error::Error; pub use integer::Integer; @@ -45,6 +46,9 @@ pub enum Value { /// A map Map(Vec<(Value, Value)>), + + /// A Simple Value + Simple(u8), } impl Value { @@ -308,7 +312,9 @@ impl Value { /// ``` pub fn as_bool(&self) -> Option { match *self { - Value::Bool(b) => Some(b), + Self::Bool(b) => Some(b), + Self::Simple(FALSE) => Some(false), + Self::Simple(TRUE) => Some(true), _ => None, } } @@ -327,12 +333,14 @@ impl Value { /// ``` pub fn into_bool(self) -> Result { match self { - Value::Bool(b) => Ok(b), + Self::Bool(b) => Ok(b), + Self::Simple(FALSE) => Ok(false), + Self::Simple(TRUE) => Ok(true), other => Err(other), } } - /// Returns true if the `Value` is a `Null`. Returns false otherwise. + /// Returns true if the `Value` is a `Null` or `Undefined`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; @@ -342,7 +350,10 @@ impl Value { /// assert!(value.is_null()); /// ``` pub fn is_null(&self) -> bool { - matches!(self, Value::Null) + matches!( + self, + Value::Null | Value::Simple(NULL) | Value::Simple(UNDEFINED) + ) } /// Returns true if the `Value` is a `Tag`. Returns false otherwise. diff --git a/ciborium/src/value/ser.rs b/ciborium/src/value/ser.rs index 99e6587..3cfb060 100644 --- a/ciborium/src/value/ser.rs +++ b/ciborium/src/value/ser.rs @@ -5,6 +5,7 @@ use super::{Error, Value}; use alloc::{vec, vec::Vec}; use ::serde::ser::{self, SerializeMap as _, SerializeSeq as _, SerializeTupleVariant as _}; +use ciborium_ll::simple::{FALSE, NULL, TRUE}; impl ser::Serialize for Value { #[inline] @@ -12,8 +13,10 @@ impl ser::Serialize for Value { match self { Value::Bytes(x) => serializer.serialize_bytes(x), Value::Bool(x) => serializer.serialize_bool(*x), + Value::Simple(FALSE) => serializer.serialize_bool(false), + Value::Simple(TRUE) => serializer.serialize_bool(true), Value::Text(x) => serializer.serialize_str(x), - Value::Null => serializer.serialize_unit(), + Value::Null | Value::Simple(NULL) => serializer.serialize_unit(), Value::Tag(t, v) => { let mut acc = serializer.serialize_tuple_variant("@@TAG@@", 0, "@@TAGGED@@", 2)?; @@ -74,6 +77,8 @@ impl ser::Serialize for Value { map.end() } + + Value::Simple(x) => serializer.serialize_newtype_struct("@@SIMPLE@@", x), } } } @@ -174,10 +179,16 @@ impl ser::Serializer for Serializer<()> { #[inline] fn serialize_newtype_struct( self, - _name: &'static str, + name: &'static str, value: &U, ) -> Result { - value.serialize(self) + match name { + "@@SIMPLE@@" => match value.serialize(crate::simple::Serializer) { + Ok(x) => Ok(Value::Simple(x)), + _ => Err(Error::Custom("expected simple value".into())), + }, + _ => value.serialize(self), + } } #[inline] diff --git a/ciborium/tests/macro.rs b/ciborium/tests/macro.rs index a73fc84..dbd38bf 100644 --- a/ciborium/tests/macro.rs +++ b/ciborium/tests/macro.rs @@ -4,7 +4,7 @@ extern crate alloc; use ciborium::{ cbor, - value::{Integer, Value, Value::Null}, + value::{Integer, Value}, }; use rstest::rstest; @@ -47,7 +47,7 @@ macro_rules! arr { // Simple array formulations case(arr![], cbor!([]).unwrap()), - case(arr![Null], cbor!([null]).unwrap()), + case(arr![Value::Null], cbor!([null]).unwrap()), case(arr![true], cbor!([true]).unwrap()), case(arr![false], cbor!([false]).unwrap()), case(arr!["foo"], cbor!(["foo"]).unwrap()), @@ -61,19 +61,19 @@ macro_rules! arr { case(arr![map! {1=>2,3=>4}], cbor!([{1=>2,3=>4}]).unwrap()), // Two-item array formluations - case(arr![Null, Null], cbor!([null, null]).unwrap()), - case(arr![Null, true], cbor!([null, true]).unwrap()), - case(arr![Null, false], cbor!([null, false]).unwrap()), - case(arr![Null, "foo"], cbor!([null, "foo"]).unwrap()), - case(arr![Null, 123], cbor!([null, 123]).unwrap()), - case(arr![Null, -123], cbor!([null, -123]).unwrap()), - case(arr![Null, 1.23], cbor!([null, 1.23]).unwrap()), - case(arr![Null, -1.23], cbor!([null, -1.23]).unwrap()), - case(arr![Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), - case(arr![Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), - case(arr![Null, [1, 2]], cbor!([null, [1, 2]]).unwrap()), - case(arr![Null, map! {1=>2,3=>4}], cbor!([null, {1=>2,3=>4}]).unwrap()), - case(arr![true, Null], cbor!([true, null]).unwrap()), + case(arr![Value::Null, Value::Null], cbor!([null, null]).unwrap()), + case(arr![Value::Null, true], cbor!([null, true]).unwrap()), + case(arr![Value::Null, false], cbor!([null, false]).unwrap()), + case(arr![Value::Null, "foo"], cbor!([null, "foo"]).unwrap()), + case(arr![Value::Null, 123], cbor!([null, 123]).unwrap()), + case(arr![Value::Null, -123], cbor!([null, -123]).unwrap()), + case(arr![Value::Null, 1.23], cbor!([null, 1.23]).unwrap()), + case(arr![Value::Null, -1.23], cbor!([null, -1.23]).unwrap()), + case(arr![Value::Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), + case(arr![Value::Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), + case(arr![Value::Null, [1, 2]], cbor!([null, [1, 2]]).unwrap()), + case(arr![Value::Null, map! {1=>2,3=>4}], cbor!([null, {1=>2,3=>4}]).unwrap()), + case(arr![true, Value::Null], cbor!([true, null]).unwrap()), case(arr![true, true], cbor!([true, true]).unwrap()), case(arr![true, false], cbor!([true, false]).unwrap()), case(arr![true, "foo"], cbor!([true, "foo"]).unwrap()), @@ -85,7 +85,7 @@ macro_rules! arr { case(arr![true, 2.5e+1], cbor!([true, 2.5e+1]).unwrap()), case(arr![true, [1, 2]], cbor!([true, [1, 2]]).unwrap()), case(arr![true, map! {1=>2,3=>4}], cbor!([true, {1=>2,3=>4}]).unwrap()), - case(arr![false, Null], cbor!([false, null]).unwrap()), + case(arr![false, Value::Null], cbor!([false, null]).unwrap()), case(arr![false, true], cbor!([false, true]).unwrap()), case(arr![false, false], cbor!([false, false]).unwrap()), case(arr![false, "foo"], cbor!([false, "foo"]).unwrap()), @@ -97,7 +97,7 @@ macro_rules! arr { case(arr![false, 2.5e+1], cbor!([false, 2.5e+1]).unwrap()), case(arr![false, [1, 2]], cbor!([false, [1, 2]]).unwrap()), case(arr![false, map! {1=>2,3=>4}], cbor!([false, {1=>2,3=>4}]).unwrap()), - case(arr!["foo", Null], cbor!(["foo", null]).unwrap()), + case(arr!["foo", Value::Null], cbor!(["foo", null]).unwrap()), case(arr!["foo", true], cbor!(["foo", true]).unwrap()), case(arr!["foo", false], cbor!(["foo", false]).unwrap()), case(arr!["foo", "foo"], cbor!(["foo", "foo"]).unwrap()), @@ -109,7 +109,7 @@ macro_rules! arr { case(arr!["foo", 2.5e+1], cbor!(["foo", 2.5e+1]).unwrap()), case(arr!["foo", [1, 2]], cbor!(["foo", [1, 2]]).unwrap()), case(arr!["foo", map! {1=>2,3=>4}], cbor!(["foo", {1=>2,3=>4}]).unwrap()), - case(arr![123, Null], cbor!([123, null]).unwrap()), + case(arr![123, Value::Null], cbor!([123, null]).unwrap()), case(arr![123, true], cbor!([123, true]).unwrap()), case(arr![123, false], cbor!([123, false]).unwrap()), case(arr![123, "foo"], cbor!([123, "foo"]).unwrap()), @@ -121,7 +121,7 @@ macro_rules! arr { case(arr![123, 2.5e+1], cbor!([123, 2.5e+1]).unwrap()), case(arr![123, [1, 2]], cbor!([123, [1, 2]]).unwrap()), case(arr![123, map! {1=>2,3=>4}], cbor!([123, {1=>2,3=>4}]).unwrap()), - case(arr![-123, Null], cbor!([-123, null]).unwrap()), + case(arr![-123, Value::Null], cbor!([-123, null]).unwrap()), case(arr![-123, true], cbor!([-123, true]).unwrap()), case(arr![-123, false], cbor!([-123, false]).unwrap()), case(arr![-123, "foo"], cbor!([-123, "foo"]).unwrap()), @@ -133,7 +133,7 @@ macro_rules! arr { case(arr![-123, 2.5e+1], cbor!([-123, 2.5e+1]).unwrap()), case(arr![-123, [1, 2]], cbor!([-123, [1, 2]]).unwrap()), case(arr![-123, map! {1=>2,3=>4}], cbor!([-123, {1=>2,3=>4}]).unwrap()), - case(arr![1.23, Null], cbor!([1.23, null]).unwrap()), + case(arr![1.23, Value::Null], cbor!([1.23, null]).unwrap()), case(arr![1.23, true], cbor!([1.23, true]).unwrap()), case(arr![1.23, false], cbor!([1.23, false]).unwrap()), case(arr![1.23, "foo"], cbor!([1.23, "foo"]).unwrap()), @@ -145,7 +145,7 @@ macro_rules! arr { case(arr![1.23, 2.5e+1], cbor!([1.23, 2.5e+1]).unwrap()), case(arr![1.23, [1, 2]], cbor!([1.23, [1, 2]]).unwrap()), case(arr![1.23, map! {1=>2,3=>4}], cbor!([1.23, {1=>2,3=>4}]).unwrap()), - case(arr![-1.23, Null], cbor!([-1.23, null]).unwrap()), + case(arr![-1.23, Value::Null], cbor!([-1.23, null]).unwrap()), case(arr![-1.23, true], cbor!([-1.23, true]).unwrap()), case(arr![-1.23, false], cbor!([-1.23, false]).unwrap()), case(arr![-1.23, "foo"], cbor!([-1.23, "foo"]).unwrap()), @@ -157,7 +157,7 @@ macro_rules! arr { case(arr![-1.23, 2.5e+1], cbor!([-1.23, 2.5e+1]).unwrap()), case(arr![-1.23, [1, 2]], cbor!([-1.23, [1, 2]]).unwrap()), case(arr![-1.23, map! {1=>2,3=>4}], cbor!([-1.23, {1=>2,3=>4}]).unwrap()), - case(arr![2.5e+1, Null], cbor!([2.5e+1, null]).unwrap()), + case(arr![2.5e+1, Value::Null], cbor!([2.5e+1, null]).unwrap()), case(arr![2.5e+1, true], cbor!([2.5e+1, true]).unwrap()), case(arr![2.5e+1, false], cbor!([2.5e+1, false]).unwrap()), case(arr![2.5e+1, "foo"], cbor!([2.5e+1, "foo"]).unwrap()), @@ -169,7 +169,7 @@ macro_rules! arr { case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, [1, 2]], cbor!([2.5e+1, [1, 2]]).unwrap()), case(arr![2.5e+1, map! {1=>2,3=>4}], cbor!([2.5e+1, {1=>2,3=>4}]).unwrap()), - case(arr![2.5e+1, Null], cbor!([2.5e+1, null]).unwrap()), + case(arr![2.5e+1, Value::Null], cbor!([2.5e+1, null]).unwrap()), case(arr![2.5e+1, true], cbor!([2.5e+1, true]).unwrap()), case(arr![2.5e+1, false], cbor!([2.5e+1, false]).unwrap()), case(arr![2.5e+1, "foo"], cbor!([2.5e+1, "foo"]).unwrap()), @@ -181,7 +181,7 @@ macro_rules! arr { case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, [1, 2]], cbor!([2.5e+1, [1, 2]]).unwrap()), case(arr![2.5e+1, map! {1=>2,3=>4}], cbor!([2.5e+1, {1=>2,3=>4}]).unwrap()), - case(arr![[1, 2], Null], cbor!([[1, 2], null]).unwrap()), + case(arr![[1, 2], Value::Null], cbor!([[1, 2], null]).unwrap()), case(arr![[1, 2], true], cbor!([[1, 2], true]).unwrap()), case(arr![[1, 2], false], cbor!([[1, 2], false]).unwrap()), case(arr![[1, 2], "foo"], cbor!([[1, 2], "foo"]).unwrap()), @@ -193,7 +193,7 @@ macro_rules! arr { case(arr![[1, 2], 2.5e+1], cbor!([[1, 2], 2.5e+1]).unwrap()), case(arr![[1, 2], [1, 2]], cbor!([[1, 2], [1, 2]]).unwrap()), case(arr![[1, 2], map! {1=>2,3=>4}], cbor!([[1, 2], {1=>2,3=>4}]).unwrap()), - case(arr![map! {1=>2,3=>4}, Null], cbor!([{1=>2,3=>4}, null]).unwrap()), + case(arr![map! {1=>2,3=>4}, Value::Null], cbor!([{1=>2,3=>4}, null]).unwrap()), case(arr![map! {1=>2,3=>4}, true], cbor!([{1=>2,3=>4}, true]).unwrap()), case(arr![map! {1=>2,3=>4}, false], cbor!([{1=>2,3=>4}, false]).unwrap()), case(arr![map! {1=>2,3=>4}, "foo"], cbor!([{1=>2,3=>4}, "foo"]).unwrap()), @@ -208,19 +208,19 @@ macro_rules! arr { // Map formulations case(map! {}, cbor!({}).unwrap()), - case(map! {Null => Null}, cbor!({ null => null }).unwrap()), - case(map! {Null => true}, cbor!({ null => true }).unwrap()), - case(map! {Null => false}, cbor!({ null => false }).unwrap()), - case(map! {Null => "foo"}, cbor!({ null => "foo" }).unwrap()), - case(map! {Null => 123}, cbor!({ null => 123 }).unwrap()), - case(map! {Null => -123}, cbor!({ null => -123 }).unwrap()), - case(map! {Null => 1.23}, cbor!({ null => 1.23 }).unwrap()), - case(map! {Null => -1.23}, cbor!({ null => -1.23 }).unwrap()), - case(map! {Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), - case(map! {Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), - case(map! {Null => [1, 2]}, cbor!({ null => [1, 2] }).unwrap()), - case(map! {Null => map! {1=>2,3=>4}}, cbor!({ null => {1=>2,3=>4} }).unwrap()), - case(map! {true => Null}, cbor!({ true => null }).unwrap()), + case(map! {Value::Null => Value::Null}, cbor!({ null => null }).unwrap()), + case(map! {Value::Null => true}, cbor!({ null => true }).unwrap()), + case(map! {Value::Null => false}, cbor!({ null => false }).unwrap()), + case(map! {Value::Null => "foo"}, cbor!({ null => "foo" }).unwrap()), + case(map! {Value::Null => 123}, cbor!({ null => 123 }).unwrap()), + case(map! {Value::Null => -123}, cbor!({ null => -123 }).unwrap()), + case(map! {Value::Null => 1.23}, cbor!({ null => 1.23 }).unwrap()), + case(map! {Value::Null => -1.23}, cbor!({ null => -1.23 }).unwrap()), + case(map! {Value::Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), + case(map! {Value::Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), + case(map! {Value::Null => [1, 2]}, cbor!({ null => [1, 2] }).unwrap()), + case(map! {Value::Null => map! {1=>2,3=>4}}, cbor!({ null => {1=>2,3=>4} }).unwrap()), + case(map! {true => Value::Null}, cbor!({ true => null }).unwrap()), case(map! {true => true}, cbor!({ true => true }).unwrap()), case(map! {true => false}, cbor!({ true => false }).unwrap()), case(map! {true => "foo"}, cbor!({ true => "foo" }).unwrap()), @@ -232,7 +232,7 @@ macro_rules! arr { case(map! {true => 2.5e+1}, cbor!({ true => 2.5e+1 }).unwrap()), case(map! {true => [1, 2]}, cbor!({ true => [1, 2] }).unwrap()), case(map! {true => map! {1=>2,3=>4}}, cbor!({ true => {1=>2,3=>4} }).unwrap()), - case(map! {false => Null}, cbor!({ false => null }).unwrap()), + case(map! {false => Value::Null}, cbor!({ false => null }).unwrap()), case(map! {false => true}, cbor!({ false => true }).unwrap()), case(map! {false => false}, cbor!({ false => false }).unwrap()), case(map! {false => "foo"}, cbor!({ false => "foo" }).unwrap()), @@ -244,7 +244,7 @@ macro_rules! arr { case(map! {false => 2.5e+1}, cbor!({ false => 2.5e+1 }).unwrap()), case(map! {false => [1, 2]}, cbor!({ false => [1, 2] }).unwrap()), case(map! {false => map! {1=>2,3=>4}}, cbor!({ false => {1=>2,3=>4} }).unwrap()), - case(map! {"foo" => Null}, cbor!({ "foo" => null }).unwrap()), + case(map! {"foo" => Value::Null}, cbor!({ "foo" => null }).unwrap()), case(map! {"foo" => true}, cbor!({ "foo" => true }).unwrap()), case(map! {"foo" => false}, cbor!({ "foo" => false }).unwrap()), case(map! {"foo" => "foo"}, cbor!({ "foo" => "foo" }).unwrap()), @@ -256,7 +256,7 @@ macro_rules! arr { case(map! {"foo" => 2.5e+1}, cbor!({ "foo" => 2.5e+1 }).unwrap()), case(map! {"foo" => [1, 2]}, cbor!({ "foo" => [1, 2] }).unwrap()), case(map! {"foo" => map! {1=>2,3=>4}}, cbor!({ "foo" => {1=>2,3=>4} }).unwrap()), - case(map! {123 => Null}, cbor!({ 123 => null }).unwrap()), + case(map! {123 => Value::Null}, cbor!({ 123 => null }).unwrap()), case(map! {123 => true}, cbor!({ 123 => true }).unwrap()), case(map! {123 => false}, cbor!({ 123 => false }).unwrap()), case(map! {123 => "foo"}, cbor!({ 123 => "foo" }).unwrap()), @@ -268,7 +268,7 @@ macro_rules! arr { case(map! {123 => 2.5e+1}, cbor!({ 123 => 2.5e+1 }).unwrap()), case(map! {123 => [1, 2]}, cbor!({ 123 => [1, 2] }).unwrap()), case(map! {123 => map! {1=>2,3=>4}}, cbor!({ 123 => {1=>2,3=>4} }).unwrap()), - case(map! {-123 => Null}, cbor!({ -123 => null }).unwrap()), + case(map! {-123 => Value::Null}, cbor!({ -123 => null }).unwrap()), case(map! {-123 => true}, cbor!({ -123 => true }).unwrap()), case(map! {-123 => false}, cbor!({ -123 => false }).unwrap()), case(map! {-123 => "foo"}, cbor!({ -123 => "foo" }).unwrap()), @@ -280,7 +280,7 @@ macro_rules! arr { case(map! {-123 => 2.5e+1}, cbor!({ -123 => 2.5e+1 }).unwrap()), case(map! {-123 => [1, 2]}, cbor!({ -123 => [1, 2] }).unwrap()), case(map! {-123 => map! {1=>2,3=>4}}, cbor!({ -123 => {1=>2,3=>4} }).unwrap()), - case(map! {1.23 => Null}, cbor!({ 1.23 => null }).unwrap()), + case(map! {1.23 => Value::Null}, cbor!({ 1.23 => null }).unwrap()), case(map! {1.23 => true}, cbor!({ 1.23 => true }).unwrap()), case(map! {1.23 => false}, cbor!({ 1.23 => false }).unwrap()), case(map! {1.23 => "foo"}, cbor!({ 1.23 => "foo" }).unwrap()), @@ -292,7 +292,7 @@ macro_rules! arr { case(map! {1.23 => 2.5e+1}, cbor!({ 1.23 => 2.5e+1 }).unwrap()), case(map! {1.23 => [1, 2]}, cbor!({ 1.23 => [1, 2] }).unwrap()), case(map! {1.23 => map! {1=>2,3=>4}}, cbor!({ 1.23 => {1=>2,3=>4} }).unwrap()), - case(map! {-1.23 => Null}, cbor!({ -1.23 => null }).unwrap()), + case(map! {-1.23 => Value::Null}, cbor!({ -1.23 => null }).unwrap()), case(map! {-1.23 => true}, cbor!({ -1.23 => true }).unwrap()), case(map! {-1.23 => false}, cbor!({ -1.23 => false }).unwrap()), case(map! {-1.23 => "foo"}, cbor!({ -1.23 => "foo" }).unwrap()), @@ -304,7 +304,7 @@ macro_rules! arr { case(map! {-1.23 => 2.5e+1}, cbor!({ -1.23 => 2.5e+1 }).unwrap()), case(map! {-1.23 => [1, 2]}, cbor!({ -1.23 => [1, 2] }).unwrap()), case(map! {-1.23 => map! {1=>2,3=>4}}, cbor!({ -1.23 => {1=>2,3=>4} }).unwrap()), - case(map! {2.5e+1 => Null}, cbor!({ 2.5e+1 => null }).unwrap()), + case(map! {2.5e+1 => Value::Null}, cbor!({ 2.5e+1 => null }).unwrap()), case(map! {2.5e+1 => true}, cbor!({ 2.5e+1 => true }).unwrap()), case(map! {2.5e+1 => false}, cbor!({ 2.5e+1 => false }).unwrap()), case(map! {2.5e+1 => "foo"}, cbor!({ 2.5e+1 => "foo" }).unwrap()), @@ -316,7 +316,7 @@ macro_rules! arr { case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => [1, 2]}, cbor!({ 2.5e+1 => [1, 2] }).unwrap()), case(map! {2.5e+1 => map! {1=>2,3=>4}}, cbor!({ 2.5e+1 => {1=>2,3=>4} }).unwrap()), - case(map! {2.5e+1 => Null}, cbor!({ 2.5e+1 => null }).unwrap()), + case(map! {2.5e+1 => Value::Null}, cbor!({ 2.5e+1 => null }).unwrap()), case(map! {2.5e+1 => true}, cbor!({ 2.5e+1 => true }).unwrap()), case(map! {2.5e+1 => false}, cbor!({ 2.5e+1 => false }).unwrap()), case(map! {2.5e+1 => "foo"}, cbor!({ 2.5e+1 => "foo" }).unwrap()), @@ -328,7 +328,7 @@ macro_rules! arr { case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => [1, 2]}, cbor!({ 2.5e+1 => [1, 2] }).unwrap()), case(map! {2.5e+1 => map! {1=>2,3=>4}}, cbor!({ 2.5e+1 => {1=>2,3=>4} }).unwrap()), - case(map! {[1, 2] => Null}, cbor!({ [1, 2] => null }).unwrap()), + case(map! {[1, 2] => Value::Null}, cbor!({ [1, 2] => null }).unwrap()), case(map! {[1, 2] => true}, cbor!({ [1, 2] => true }).unwrap()), case(map! {[1, 2] => false}, cbor!({ [1, 2] => false }).unwrap()), case(map! {[1, 2] => "foo"}, cbor!({ [1, 2] => "foo" }).unwrap()), @@ -340,7 +340,7 @@ macro_rules! arr { case(map! {[1, 2] => 2.5e+1}, cbor!({ [1, 2] => 2.5e+1 }).unwrap()), case(map! {[1, 2] => [1, 2]}, cbor!({ [1, 2] => [1, 2] }).unwrap()), case(map! {[1, 2] => map! {1=>2,3=>4}}, cbor!({ [1, 2] => {1=>2,3=>4} }).unwrap()), - case(map! {map! {1=>2,3=>4} => Null}, cbor!({ {1=>2,3=>4} => null }).unwrap()), + case(map! {map! {1=>2,3=>4} => Value::Null}, cbor!({ {1=>2,3=>4} => null }).unwrap()), case(map! {map! {1=>2,3=>4} => true}, cbor!({ {1=>2,3=>4} => true }).unwrap()), case(map! {map! {1=>2,3=>4} => false}, cbor!({ {1=>2,3=>4} => false }).unwrap()), case(map! {map! {1=>2,3=>4} => "foo"}, cbor!({ {1=>2,3=>4} => "foo" }).unwrap()), diff --git a/ciborium/tests/simple.rs b/ciborium/tests/simple.rs new file mode 100644 index 0000000..7e8f5b0 --- /dev/null +++ b/ciborium/tests/simple.rs @@ -0,0 +1,58 @@ +use ciborium::{from_reader, into_writer, Value}; +use ciborium_ll::simple::UNDEFINED; +use rstest::rstest; +use serde::{Deserialize, Serialize}; + +#[rstest] +#[case("e0", Value::Simple(0))] +#[case("e1", Value::Simple(1))] +#[case("f0", Value::Simple(16))] +#[case("f1", Value::Simple(17))] +#[case("f2", Value::Simple(18))] +#[case("f3", Value::Simple(19))] +#[case("f4", Value::Bool(false))] +#[case("f5", Value::Bool(true))] +#[case("f6", Value::Null)] +#[case("f7", Value::Simple(UNDEFINED))] +#[case("f8ff", Value::Simple(255))] +#[case("f90000", Value::Float(0f64))] +#[case("f93c00", Value::Float(1f64))] +#[case("fa00000000", Value::Float(0f64))] +#[case("fa3f800000", Value::Float(1f64))] +fn deserialize_value(#[case] bytes: &str, #[case] expected: Value) { + let bytes = hex::decode(bytes).unwrap(); + let actual: Value = from_reader(&bytes[..]).unwrap(); + assert_eq!(actual, expected); +} + +#[rstest] +#[case(Value::Simple(0), "e0")] +#[case(Value::Simple(1), "e1")] +#[case(Value::Simple(16), "f0")] +#[case(Value::Simple(17), "f1")] +#[case(Value::Simple(18), "f2")] +#[case(Value::Simple(19), "f3")] +#[case(Value::Bool(false), "f4")] +#[case(Value::Bool(true), "f5")] +#[case(Value::Null, "f6")] +#[case(Value::Simple(UNDEFINED), "f7")] +#[case(Value::Simple(255), "f8ff")] +#[case(Value::Float(0f64), "f90000")] +#[case(Value::Float(1f64), "f93c00")] +fn serialize_value(#[case] value: Value, #[case] bytes: &str) { + let expected = hex::decode(bytes).unwrap(); + let mut actual = Vec::::new(); + into_writer(&value, &mut actual).unwrap(); + assert_eq!(actual, expected); +} + +#[test] +fn do_not_accept_simple_for_integer() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + pub struct NotSimple(u8); + + let bytes: [u8; 1] = [0xE0]; + let my_simple: Result = from_reader(&bytes[..]); + + assert!(my_simple.is_err()); +} diff --git a/ciborium/tests/value.rs b/ciborium/tests/value.rs new file mode 100644 index 0000000..d50d404 --- /dev/null +++ b/ciborium/tests/value.rs @@ -0,0 +1,80 @@ +use ciborium::ser::into_writer; +use ciborium::value::Value; +use ciborium_ll::simple::{FALSE, NULL, TRUE, UNDEFINED}; +use rstest::{fixture, rstest}; +use serde::Serialize; +use std::collections::HashMap; + +#[derive(Serialize)] +struct ExampleStruct { + a: Vec, + b: f64, + c: HashMap, +} + +#[fixture] +fn struct_instance_1() -> ExampleStruct { + let mut map = HashMap::new(); + map.insert(15, Value::Bool(false)); + ExampleStruct { + a: vec!["Hello".to_string(), "lol".to_string()], + b: 3.14, + c: map, + } +} + +#[fixture] +fn struct_instance_2() -> ExampleStruct { + let mut map = HashMap::new(); + map.insert(15, Value::Bool(false)); + ExampleStruct { + a: vec!["Hello".to_string()], + b: 4.2, + c: map, + } +} + +#[rstest( + item, + bytes, + case(Value::Simple(0), "E0"), + case(Value::Simple(1), "E1"), + case(Value::Simple(2), "E2"), + case(Value::Simple(3), "E3"), + case(Value::Simple(4), "E4"), + case(Value::Simple(5), "E5"), + case(Value::Simple(6), "E6"), + case(Value::Simple(7), "E7"), + case(Value::Simple(8), "E8"), + case(Value::Simple(9), "E9"), + case(Value::Simple(10), "EA"), + case(Value::Simple(11), "EB"), + case(Value::Simple(12), "EC"), + case(Value::Simple(13), "ED"), + case(Value::Simple(14), "EE"), + case(Value::Simple(15), "EF"), + case(Value::Simple(16), "F0"), + case(Value::Simple(17), "F1"), + case(Value::Simple(18), "F2"), + case(Value::Simple(19), "F3"), + case(Value::Bool(false), "F4"), + case(Value::Simple(FALSE), "F4"), + case(Value::Bool(true), "F5"), + case(Value::Simple(TRUE), "F5"), + case(Value::Null, "F6"), + case(Value::Simple(NULL), "F6"), + case(Value::Simple(UNDEFINED), "F7"), + case(Value::Simple(32), "F820"), + case(Value::Simple(33), "F821"), + case(Value::Simple(127), "F87F"), + case(Value::Simple(128), "F880"), + case(Value::Simple(255), "F8FF") +)] +fn serialize(item: Value, bytes: &str) { + let bytes = hex::decode(bytes).unwrap(); + + let mut writer: Vec = Vec::new(); + into_writer(&item, &mut writer).unwrap(); + + assert_eq!(writer, bytes); +}