diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/de/mod.rs | 127 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/ser/mod.rs | 3 | ||||
-rw-r--r-- | src/tag.rs | 14 | ||||
-rw-r--r-- | src/value/canonical.rs | 124 | ||||
-rw-r--r-- | src/value/de.rs | 6 | ||||
-rw-r--r-- | src/value/integer.rs | 59 | ||||
-rw-r--r-- | src/value/mod.rs | 213 | ||||
-rw-r--r-- | src/value/ser.rs | 14 |
9 files changed, 513 insertions, 63 deletions
diff --git a/src/de/mod.rs b/src/de/mod.rs index 63e5109..b68458d 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -51,7 +51,7 @@ struct Deserializer<'b, R: Read> { recurse: usize, } -impl<'de, 'a, 'b, R: Read> Deserializer<'b, R> +impl<'a, R: Read> Deserializer<'a, R> where R::Error: core::fmt::Debug, { @@ -348,7 +348,7 @@ where let mut segments = self.decoder.text(len); while let Some(mut segment) = segments.pull()? { - while let Some(chunk) = segment.pull(&mut self.scratch)? { + while let Some(chunk) = segment.pull(self.scratch)? { buffer.push_str(chunk); } } @@ -371,6 +371,11 @@ where visitor.visit_bytes(&self.scratch[..len]) } + Header::Array(len) => self.recurse(|me| { + let access = Access(me, len); + visitor.visit_seq(access) + }), + header => Err(header.expected("bytes")), }; } @@ -389,7 +394,7 @@ where let mut segments = self.decoder.bytes(len); while let Some(mut segment) = segments.pull()? { - while let Some(chunk) = segment.pull(&mut self.scratch)? { + while let Some(chunk) = segment.pull(self.scratch)? { buffer.extend_from_slice(chunk); } } @@ -397,7 +402,12 @@ where visitor.visit_byte_buf(buffer) } - header => Err(header.expected("expected byte buffer")), + Header::Array(len) => self.recurse(|me| { + let access = Access(me, len); + visitor.visit_seq(access) + }), + + header => Err(header.expected("byte buffer")), }; } } @@ -412,6 +422,19 @@ where visitor.visit_seq(access) }), + Header::Bytes(len) => { + let mut buffer = Vec::new(); + + let mut segments = self.decoder.bytes(len); + while let Some(mut segment) = segments.pull()? { + while let Some(chunk) = segment.pull(self.scratch)? { + buffer.extend_from_slice(chunk); + } + } + + visitor.visit_seq(BytesAccess::<R>(0, buffer, core::marker::PhantomData)) + } + header => Err(header.expected("array")), }; } @@ -462,7 +485,28 @@ where self, visitor: V, ) -> Result<V::Value, Self::Error> { - self.deserialize_str(visitor) + loop { + let offset = self.decoder.offset(); + + return match self.decoder.pull()? { + Header::Tag(..) => continue, + + Header::Text(Some(len)) if len <= self.scratch.len() => { + self.decoder.read_exact(&mut self.scratch[..len])?; + + match core::str::from_utf8(&self.scratch[..len]) { + Ok(s) => visitor.visit_str(s), + Err(..) => Err(Error::Syntax(offset)), + } + } + Header::Bytes(Some(len)) if len <= self.scratch.len() => { + self.decoder.read_exact(&mut self.scratch[..len])?; + visitor.visit_bytes(&self.scratch[..len]) + } + + header => Err(header.expected("str or bytes")), + }; + } } fn deserialize_ignored_any<V: de::Visitor<'de>>( @@ -474,16 +518,13 @@ where #[inline] fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { - loop { - return match self.decoder.pull()? { - Header::Simple(simple::UNDEFINED) => visitor.visit_none(), - Header::Simple(simple::NULL) => visitor.visit_none(), - Header::Tag(..) => continue, - header => { - self.decoder.push(header); - visitor.visit_some(self) - } - }; + match self.decoder.pull()? { + Header::Simple(simple::UNDEFINED) => visitor.visit_none(), + Header::Simple(simple::NULL) => visitor.visit_none(), + header => { + self.decoder.push(header); + visitor.visit_some(self) + } } } @@ -683,6 +724,36 @@ where } } +struct BytesAccess<R: Read>(usize, Vec<u8>, core::marker::PhantomData<R>); + +impl<'de, R: Read> de::SeqAccess<'de> for BytesAccess<R> +where + R::Error: core::fmt::Debug, +{ + type Error = Error<R::Error>; + + #[inline] + fn next_element_seed<U: de::DeserializeSeed<'de>>( + &mut self, + seed: U, + ) -> Result<Option<U::Value>, Self::Error> { + use de::IntoDeserializer; + + if self.0 < self.1.len() { + let byte = self.1[self.0]; + self.0 += 1; + seed.deserialize(byte.into_deserializer()).map(Some) + } else { + Ok(None) + } + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.1.len() - self.0) + } +} + struct TagAccess<'a, 'b, R: Read>(&'a mut Deserializer<'b, R>, usize); impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &mut TagAccess<'a, 'b, R> @@ -746,7 +817,7 @@ where /// Deserializes as CBOR from a type with [`impl ciborium_io::Read`](ciborium_io::Read) #[inline] -pub fn from_reader<'de, T: de::Deserialize<'de>, R: Read>(reader: R) -> Result<T, Error<R::Error>> +pub fn from_reader<T: de::DeserializeOwned, R: Read>(reader: R) -> Result<T, Error<R::Error>> where R::Error: core::fmt::Debug, { @@ -760,3 +831,27 @@ where T::deserialize(&mut reader) } + +/// Deserializes as CBOR from a type with [`impl ciborium_io::Read`](ciborium_io::Read), with +/// a specified maximum recursion limit. Inputs that are nested beyond the specified limit +/// will result in [`Error::RecursionLimitExceeded`] . +/// +/// Set a high recursion limit at your own risk (of stack exhaustion)! +#[inline] +pub fn from_reader_with_recursion_limit<T: de::DeserializeOwned, R: Read>( + reader: R, + recurse_limit: usize, +) -> Result<T, Error<R::Error>> +where + R::Error: core::fmt::Debug, +{ + let mut scratch = [0; 4096]; + + let mut reader = Deserializer { + decoder: reader.into(), + scratch: &mut scratch, + recurse: recurse_limit, + }; + + T::deserialize(&mut reader) +} @@ -6,12 +6,12 @@ //! //! # Quick Start //! -//! You're probably looking for [`de::from_reader()`](crate::de::from_reader) -//! and [`ser::into_writer()`](crate::ser::into_writer), which are +//! You're probably looking for [`from_reader()`](crate::de::from_reader) +//! and [`into_writer()`](crate::ser::into_writer), which are //! the main functions. Note that byte slices are also readers and writers and can be //! passed to these functions just as streams can. //! -//! For dynamic CBOR value creation/inspection, see [`value::Value`](crate::value::Value). +//! For dynamic CBOR value creation/inspection, see [`Value`](crate::value::Value). //! //! # Design Decisions //! @@ -97,6 +97,16 @@ pub mod ser; pub mod tag; pub mod value; +// Re-export the [items recommended by serde](https://serde.rs/conventions.html). +#[doc(inline)] +pub use crate::de::from_reader; + +#[doc(inline)] +pub use crate::ser::into_writer; + +#[doc(inline)] +pub use crate::value::Value; + /// Build a `Value` conveniently. /// /// The syntax should be intuitive if you are familiar with JSON. You can also diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 0488d72..ff3d118 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -495,6 +495,5 @@ where W::Error: core::fmt::Debug, { let mut encoder = Serializer::from(writer); - value.serialize(&mut encoder)?; - Ok(encoder.0.flush()?) + value.serialize(&mut encoder) } @@ -426,7 +426,7 @@ impl ser::Serializer for Serializer { } } -impl<'a> ser::SerializeSeq for Serializer { +impl ser::SerializeSeq for Serializer { type Ok = u64; type Error = Error; @@ -441,7 +441,7 @@ impl<'a> ser::SerializeSeq for Serializer { } } -impl<'a> ser::SerializeTuple for Serializer { +impl ser::SerializeTuple for Serializer { type Ok = u64; type Error = Error; @@ -456,7 +456,7 @@ impl<'a> ser::SerializeTuple for Serializer { } } -impl<'a> ser::SerializeTupleStruct for Serializer { +impl ser::SerializeTupleStruct for Serializer { type Ok = u64; type Error = Error; @@ -471,7 +471,7 @@ impl<'a> ser::SerializeTupleStruct for Serializer { } } -impl<'a> ser::SerializeTupleVariant for Serializer { +impl ser::SerializeTupleVariant for Serializer { type Ok = u64; type Error = Error; @@ -486,7 +486,7 @@ impl<'a> ser::SerializeTupleVariant for Serializer { } } -impl<'a> ser::SerializeMap for Serializer { +impl ser::SerializeMap for Serializer { type Ok = u64; type Error = Error; @@ -506,7 +506,7 @@ impl<'a> ser::SerializeMap for Serializer { } } -impl<'a> ser::SerializeStruct for Serializer { +impl ser::SerializeStruct for Serializer { type Ok = u64; type Error = Error; @@ -525,7 +525,7 @@ impl<'a> ser::SerializeStruct for Serializer { } } -impl<'a> ser::SerializeStructVariant for Serializer { +impl ser::SerializeStructVariant for Serializer { type Ok = u64; type Error = Error; diff --git a/src/value/canonical.rs b/src/value/canonical.rs new file mode 100644 index 0000000..f1196f4 --- /dev/null +++ b/src/value/canonical.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 + +use crate::value::Value; +use alloc::vec::Vec; +use core::cmp::Ordering; +use serde::{de, ser}; + +/// Manually serialize values to compare them. +fn serialized_canonical_cmp(v1: &Value, v2: &Value) -> Ordering { + // There is an optimization to be done here, but it would take a lot more code + // and using mixing keys, Arrays or Maps as CanonicalValue is probably not the + // best use of this type as it is meant mainly to be used as keys. + + let mut bytes1 = Vec::new(); + let _ = crate::ser::into_writer(v1, &mut bytes1); + let mut bytes2 = Vec::new(); + let _ = crate::ser::into_writer(v2, &mut bytes2); + + match bytes1.len().cmp(&bytes2.len()) { + Ordering::Equal => bytes1.cmp(&bytes2), + x => x, + } +} + +/// Compares two values uses canonical comparison, as defined in both +/// RFC 7049 Section 3.9 (regarding key sorting) and RFC 8949 4.2.3 (as errata). +/// +/// In short, the comparison follow the following rules: +/// - If two keys have different lengths, the shorter one sorts earlier; +/// - If two keys have the same length, the one with the lower value in +/// (byte-wise) lexical order sorts earlier. +/// +/// This specific comparison allows Maps and sorting that respect these two rules. +pub fn cmp_value(v1: &Value, v2: &Value) -> Ordering { + use Value::*; + + match (v1, v2) { + (Integer(i), Integer(o)) => { + // Because of the first rule above, two numbers might be in a different + // order than regular i128 comparison. For example, 10 < -1 in + // canonical ordering, since 10 serializes to `0x0a` and -1 to `0x20`, + // and -1 < -1000 because of their lengths. + i.canonical_cmp(o) + } + (Text(s), Text(o)) => match s.len().cmp(&o.len()) { + Ordering::Equal => s.cmp(o), + x => x, + }, + (Bool(s), Bool(o)) => s.cmp(o), + (Null, Null) => Ordering::Equal, + (Tag(t, v), Tag(ot, ov)) => match Value::from(*t).partial_cmp(&Value::from(*ot)) { + Some(Ordering::Equal) | None => match v.partial_cmp(ov) { + Some(x) => x, + None => serialized_canonical_cmp(v1, v2), + }, + Some(x) => x, + }, + (_, _) => serialized_canonical_cmp(v1, v2), + } +} + +/// A CBOR Value that impl Ord and Eq to allow sorting of values as defined in both +/// RFC 7049 Section 3.9 (regarding key sorting) and RFC 8949 4.2.3 (as errata). +/// +/// Since a regular [Value] can be +#[derive(Clone, Debug)] +pub struct CanonicalValue(Value); + +impl PartialEq for CanonicalValue { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for CanonicalValue {} + +impl From<Value> for CanonicalValue { + fn from(v: Value) -> Self { + Self(v) + } +} + +impl From<CanonicalValue> for Value { + fn from(v: CanonicalValue) -> Self { + v.0 + } +} + +impl ser::Serialize for CanonicalValue { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de> de::Deserialize<'de> for CanonicalValue { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + Value::deserialize(deserializer).map(Into::into) + } + + fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error> + where + D: de::Deserializer<'de>, + { + Value::deserialize_in_place(deserializer, &mut place.0) + } +} + +impl Ord for CanonicalValue { + fn cmp(&self, other: &Self) -> Ordering { + cmp_value(&self.0, &other.0) + } +} + +impl PartialOrd for CanonicalValue { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(cmp_value(&self.0, &other.0)) + } +} diff --git a/src/value/de.rs b/src/value/de.rs index 0d30a86..6323190 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -173,7 +173,7 @@ impl<'de> de::Deserialize<'de> for Value { struct Deserializer<T>(T); -impl<'a, 'de> Deserializer<&'a Value> { +impl<'a> Deserializer<&'a Value> { fn integer<N>(&self, kind: &'static str) -> Result<N, Error> where N: TryFrom<u128>, @@ -233,7 +233,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { Value::Null => visitor.visit_none(), Value::Tag(t, v) => { - let parent: Deserializer<&Value> = Deserializer(&*v); + let parent: Deserializer<&Value> = Deserializer(v); let access = crate::tag::TagAccess::new(parent, Some(*t)); visitor.visit_enum(access) } @@ -487,7 +487,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { v => (None, v), }; - let parent: Deserializer<&Value> = Deserializer(&*val); + let parent: Deserializer<&Value> = Deserializer(val); let access = crate::tag::TagAccess::new(parent, tag); return visitor.visit_enum(access); } diff --git a/src/value/integer.rs b/src/value/integer.rs index 4dd1f3a..ef6ea3d 100644 --- a/src/value/integer.rs +++ b/src/value/integer.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 +use core::cmp::Ordering; macro_rules! implfrom { ($( $(#[$($attr:meta)+])? $t:ident)+) => { @@ -33,6 +34,64 @@ macro_rules! implfrom { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Integer(i128); +impl Integer { + /// Returns the canonical length this integer will have when serialized to bytes. + /// This is called `canonical` as it is only used for canonically comparing two + /// values. It shouldn't be used in any other context. + fn canonical_len(&self) -> usize { + let x = self.0; + + if let Ok(x) = u8::try_from(x) { + if x < 24 { + 1 + } else { + 2 + } + } else if let Ok(x) = i8::try_from(x) { + if x >= -24i8 { + 1 + } else { + 2 + } + } else if u16::try_from(x).is_ok() || i16::try_from(x).is_ok() { + 3 + } else if u32::try_from(x).is_ok() || i32::try_from(x).is_ok() { + 5 + } else if u64::try_from(x).is_ok() || i64::try_from(x).is_ok() { + 9 + } else { + // Ciborium serializes u128/i128 as BigPos if they don't fit in 64 bits. + // In this special case we have to calculate the length. + // The Tag itself will always be 1 byte. + x.to_be_bytes().len() + 1 + } + } + + /// Compare two integers as if we were to serialize them, but more efficiently. + pub fn canonical_cmp(&self, other: &Self) -> Ordering { + match self.canonical_len().cmp(&other.canonical_len()) { + Ordering::Equal => { + // Negative numbers are higher in byte-order than positive numbers. + match (self.0.is_negative(), other.0.is_negative()) { + (false, true) => Ordering::Less, + (true, false) => Ordering::Greater, + (true, true) => { + // For negative numbers the byte order puts numbers closer to 0 which + // are lexically higher, lower. So -1 < -2 when sorting by be_bytes(). + match self.0.cmp(&other.0) { + Ordering::Less => Ordering::Greater, + Ordering::Equal => Ordering::Equal, + Ordering::Greater => Ordering::Less, + } + } + (_, _) => self.0.cmp(&other.0), + } + } + x => x, + } + } +} + implfrom! { u8 u16 u32 u64 i8 i16 i32 i64 diff --git a/src/value/mod.rs b/src/value/mod.rs index 40988b0..7233026 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,12 +2,14 @@ //! A dynamic CBOR value +mod canonical; mod integer; mod de; mod error; mod ser; +pub use canonical::CanonicalValue; pub use error::Error; pub use integer::Integer; @@ -49,7 +51,7 @@ impl Value { /// Returns true if the `Value` is an `Integer`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// @@ -63,7 +65,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// @@ -77,10 +79,29 @@ impl Value { } } + /// If the `Value` is a `Integer`, returns a the associated `Integer` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::{Value, value::Integer}; + /// # + /// let value = Value::Integer(17.into()); + /// assert_eq!(value.into_integer(), Ok(Integer::from(17))); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_integer(), Err(Value::Bool(true))); + /// ``` + pub fn into_integer(self) -> Result<Integer, Self> { + match self { + Value::Integer(int) => Ok(int), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Bytes`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// @@ -94,7 +115,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// @@ -111,7 +132,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// value.as_bytes_mut().unwrap().clear(); @@ -125,10 +146,29 @@ impl Value { } } + /// If the `Value` is a `Bytes`, returns a the associated `Vec<u8>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); + /// assert_eq!(value.into_bytes(), Ok(vec![104, 101, 108, 108, 111])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_bytes(), Err(Value::Bool(true))); + /// ``` + pub fn into_bytes(self) -> Result<Vec<u8>, Self> { + match self { + Value::Bytes(vec) => Ok(vec), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Float`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Float(17.0.into()); /// @@ -142,7 +182,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Float(17.0.into()); /// @@ -156,10 +196,29 @@ impl Value { } } + /// If the `Value` is a `Float`, returns a the associated `f64` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Float(17.); + /// assert_eq!(value.into_float(), Ok(17.)); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_float(), Err(Value::Bool(true))); + /// ``` + pub fn into_float(self) -> Result<f64, Self> { + match self { + Value::Float(f) => Ok(f), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Text`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// @@ -173,7 +232,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// @@ -191,7 +250,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Text(String::from("hello")); /// value.as_text_mut().unwrap().clear(); @@ -205,10 +264,29 @@ impl Value { } } + /// If the `Value` is a `String`, returns a the associated `String` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Text(String::from("hello")); + /// assert_eq!(value.into_text().as_deref(), Ok("hello")); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_text(), Err(Value::Bool(true))); + /// ``` + pub fn into_text(self) -> Result<String, Self> { + match self { + Value::Text(s) => Ok(s), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Bool`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// @@ -222,7 +300,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// @@ -235,10 +313,29 @@ impl Value { } } + /// If the `Value` is a `Bool`, returns a the associated `bool` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Bool(false); + /// assert_eq!(value.into_bool(), Ok(false)); + /// + /// let value = Value::Float(17.); + /// assert_eq!(value.into_bool(), Err(Value::Float(17.))); + /// ``` + pub fn into_bool(self) -> Result<bool, Self> { + match self { + Value::Bool(b) => Ok(b), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Null`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Null; /// @@ -251,7 +348,7 @@ impl Value { /// Returns true if the `Value` is a `Tag`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Null)); /// @@ -265,7 +362,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// @@ -284,7 +381,7 @@ impl Value { /// to the tag `Value`. Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// @@ -300,10 +397,29 @@ impl Value { } } + /// If the `Value` is a `Tag`, returns a the associated pair of `u64` and `Box<value>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Tag(7, Box::new(Value::Float(12.))); + /// assert_eq!(value.into_tag(), Ok((7, Box::new(Value::Float(12.))))); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_tag(), Err(Value::Bool(true))); + /// ``` + pub fn into_tag(self) -> Result<(u64, Box<Value>), Self> { + match self { + Value::Tag(tag, value) => Ok((tag, value)), + other => Err(other), + } + } + /// Returns true if the `Value` is an Array. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Array( /// vec![ @@ -322,7 +438,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Array( /// vec![ @@ -336,7 +452,7 @@ impl Value { /// ``` pub fn as_array(&self) -> Option<&Vec<Value>> { match *self { - Value::Array(ref array) => Some(&*array), + Value::Array(ref array) => Some(array), _ => None, } } @@ -345,7 +461,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Array( /// vec![ @@ -364,10 +480,34 @@ impl Value { } } + /// If the `Value` is a `Array`, returns a the associated `Vec<Value>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::{Value, value::Integer}; + /// # + /// let mut value = Value::Array( + /// vec![ + /// Value::Integer(17.into()), + /// Value::Float(18.), + /// ] + /// ); + /// assert_eq!(value.into_array(), Ok(vec![Value::Integer(17.into()), Value::Float(18.)])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_array(), Err(Value::Bool(true))); + /// ``` + pub fn into_array(self) -> Result<Vec<Value>, Self> { + match self { + Value::Array(vec) => Ok(vec), + other => Err(other), + } + } + /// Returns true if the `Value` is a Map. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Map( /// vec![ @@ -385,7 +525,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Map( /// vec![ @@ -413,7 +553,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Map( /// vec![ @@ -431,6 +571,29 @@ impl Value { _ => None, } } + + /// If the `Value` is a `Map`, returns a the associated `Vec<(Value, Value)>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let mut value = Value::Map( + /// vec![ + /// (Value::Text(String::from("key")), Value::Float(18.)), + /// ] + /// ); + /// assert_eq!(value.into_map(), Ok(vec![(Value::Text(String::from("key")), Value::Float(18.))])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_map(), Err(Value::Bool(true))); + /// ``` + pub fn into_map(self) -> Result<Vec<(Value, Value)>, Self> { + match self { + Value::Map(map) => Ok(map), + other => Err(other), + } + } } macro_rules! implfrom { @@ -483,7 +646,7 @@ impl From<u128> for Value { } let mut bytes = &value.to_be_bytes()[..]; - while let Some(0) = bytes.get(0) { + while let Some(0) = bytes.first() { bytes = &bytes[1..]; } @@ -504,7 +667,7 @@ impl From<i128> for Value { }; let mut bytes = &raw.to_be_bytes()[..]; - while let Some(0) = bytes.get(0) { + while let Some(0) = bytes.first() { bytes = &bytes[1..]; } diff --git a/src/value/ser.rs b/src/value/ser.rs index 6c739d3..a399719 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -270,7 +270,7 @@ impl ser::Serializer for Serializer<()> { } } -impl<'a> ser::SerializeSeq for Serializer<Vec<Value>> { +impl ser::SerializeSeq for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -286,7 +286,7 @@ impl<'a> ser::SerializeSeq for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTuple for Serializer<Vec<Value>> { +impl ser::SerializeTuple for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -302,7 +302,7 @@ impl<'a> ser::SerializeTuple for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTupleStruct for Serializer<Vec<Value>> { +impl ser::SerializeTupleStruct for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -318,7 +318,7 @@ impl<'a> ser::SerializeTupleStruct for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { +impl ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { type Ok = Value; type Error = Error; @@ -356,7 +356,7 @@ impl<'a> ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { } } -impl<'a> ser::SerializeMap for Serializer<Map> { +impl ser::SerializeMap for Serializer<Map> { type Ok = Value; type Error = Error; @@ -381,7 +381,7 @@ impl<'a> ser::SerializeMap for Serializer<Map> { } } -impl<'a> ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { +impl ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { type Ok = Value; type Error = Error; @@ -403,7 +403,7 @@ impl<'a> ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { } } -impl<'a> ser::SerializeStructVariant for Serializer<Named<Vec<(Value, Value)>>> { +impl ser::SerializeStructVariant for Serializer<Named<Vec<(Value, Value)>>> { type Ok = Value; type Error = Error; |