// SPDX-License-Identifier: Apache-2.0 //! 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; use alloc::{boxed::Box, string::String, vec::Vec}; /// A representation of a dynamic CBOR value that can handled dynamically #[non_exhaustive] #[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum Value { /// An integer Integer(Integer), /// Bytes Bytes(Vec), /// A float Float(f64), /// A string Text(String), /// A boolean Bool(bool), /// Null Null, /// Tag Tag(u64, Box), /// An array Array(Vec), /// A map Map(Vec<(Value, Value)>), } impl Value { /// Returns true if the `Value` is an `Integer`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// /// assert!(value.is_integer()); /// ``` pub fn is_integer(&self) -> bool { self.as_integer().is_some() } /// If the `Value` is a `Integer`, returns a reference to the associated `Integer` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// /// // We can read the number /// assert_eq!(17, value.as_integer().unwrap().try_into().unwrap()); /// ``` pub fn as_integer(&self) -> Option { match self { Value::Integer(int) => Some(*int), _ => None, } } /// 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 { match self { Value::Integer(int) => Ok(int), other => Err(other), } } /// Returns true if the `Value` is a `Bytes`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// /// assert!(value.is_bytes()); /// ``` pub fn is_bytes(&self) -> bool { self.as_bytes().is_some() } /// If the `Value` is a `Bytes`, returns a reference to the associated bytes vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// /// assert_eq!(std::str::from_utf8(value.as_bytes().unwrap()).unwrap(), "hello"); /// ``` pub fn as_bytes(&self) -> Option<&Vec> { match *self { Value::Bytes(ref bytes) => Some(bytes), _ => None, } } /// If the `Value` is a `Bytes`, returns a mutable reference to the associated bytes vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let mut value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// value.as_bytes_mut().unwrap().clear(); /// /// assert_eq!(value, Value::Bytes(vec![])); /// ``` pub fn as_bytes_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Bytes(ref mut bytes) => Some(bytes), _ => None, } } /// If the `Value` is a `Bytes`, returns a the associated `Vec` 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, 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; /// # /// let value = Value::Float(17.0.into()); /// /// assert!(value.is_float()); /// ``` pub fn is_float(&self) -> bool { self.as_float().is_some() } /// If the `Value` is a `Float`, returns a reference to the associated float data. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Float(17.0.into()); /// /// // We can read the float number /// assert_eq!(value.as_float().unwrap(), 17.0_f64); /// ``` pub fn as_float(&self) -> Option { match *self { Value::Float(f) => Some(f), _ => None, } } /// 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 { match self { Value::Float(f) => Ok(f), other => Err(other), } } /// Returns true if the `Value` is a `Text`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// /// assert!(value.is_text()); /// ``` pub fn is_text(&self) -> bool { self.as_text().is_some() } /// If the `Value` is a `Text`, returns a reference to the associated `String` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// /// // We can read the String /// assert_eq!(value.as_text().unwrap(), "hello"); /// ``` pub fn as_text(&self) -> Option<&str> { match *self { Value::Text(ref s) => Some(s), _ => None, } } /// If the `Value` is a `Text`, returns a mutable reference to the associated `String` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let mut value = Value::Text(String::from("hello")); /// value.as_text_mut().unwrap().clear(); /// /// assert_eq!(value.as_text().unwrap(), &String::from("")); /// ``` pub fn as_text_mut(&mut self) -> Option<&mut String> { match *self { Value::Text(ref mut s) => Some(s), _ => None, } } /// 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 { match self { Value::Text(s) => Ok(s), other => Err(other), } } /// Returns true if the `Value` is a `Bool`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// /// assert!(value.is_bool()); /// ``` pub fn is_bool(&self) -> bool { self.as_bool().is_some() } /// If the `Value` is a `Bool`, returns a copy of the associated boolean value. Returns None /// otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// /// assert_eq!(value.as_bool().unwrap(), false); /// ``` pub fn as_bool(&self) -> Option { match *self { Value::Bool(b) => Some(b), _ => None, } } /// 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 { match self { Value::Bool(b) => Ok(b), other => Err(other), } } /// Returns true if the `Value` is a `Null`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Null; /// /// assert!(value.is_null()); /// ``` pub fn is_null(&self) -> bool { matches!(self, Value::Null) } /// Returns true if the `Value` is a `Tag`. Returns false otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Null)); /// /// assert!(value.is_tag()); /// ``` pub fn is_tag(&self) -> bool { self.as_tag().is_some() } /// If the `Value` is a `Tag`, returns the associated tag value and a reference to the tag `Value`. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// /// let (tag, data) = value.as_tag().unwrap(); /// assert_eq!(tag, 61); /// assert_eq!(data, &Value::Bytes(vec![104, 101, 108, 108, 111])); /// ``` pub fn as_tag(&self) -> Option<(u64, &Value)> { match self { Value::Tag(tag, data) => Some((*tag, data)), _ => None, } } /// If the `Value` is a `Tag`, returns the associated tag value and a mutable reference /// to the tag `Value`. Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let mut value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// /// let (tag, mut data) = value.as_tag_mut().unwrap(); /// data.as_bytes_mut().unwrap().clear(); /// assert_eq!(tag, &61); /// assert_eq!(data, &Value::Bytes(vec![])); /// ``` pub fn as_tag_mut(&mut self) -> Option<(&mut u64, &mut Value)> { match self { Value::Tag(tag, data) => Some((tag, data.as_mut())), _ => None, } } /// If the `Value` is a `Tag`, returns a the associated pair of `u64` and `Box` 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), 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; /// # /// let value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// assert!(value.is_array()); /// ``` pub fn is_array(&self) -> bool { self.as_array().is_some() } /// If the `Value` is an Array, returns a reference to the associated vector. Returns None /// otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// // The length of `value` is 2 elements. /// assert_eq!(value.as_array().unwrap().len(), 2); /// ``` pub fn as_array(&self) -> Option<&Vec> { match *self { Value::Array(ref array) => Some(array), _ => None, } } /// If the `Value` is an Array, returns a mutable reference to the associated vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let mut value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// value.as_array_mut().unwrap().clear(); /// assert_eq!(value, Value::Array(vec![])); /// ``` pub fn as_array_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Array(ref mut list) => Some(list), _ => None, } } /// If the `Value` is a `Array`, returns a the associated `Vec` 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, 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; /// # /// let value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// assert!(value.is_map()); /// ``` pub fn is_map(&self) -> bool { self.as_map().is_some() } /// If the `Value` is a Map, returns a reference to the associated Map data. Returns None /// otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// // The length of data is 1 entry (1 key/value pair). /// assert_eq!(value.as_map().unwrap().len(), 1); /// /// // The content of the first element is what we expect /// assert_eq!( /// value.as_map().unwrap().get(0).unwrap(), /// &(Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ); /// ``` pub fn as_map(&self) -> Option<&Vec<(Value, Value)>> { match *self { Value::Map(ref map) => Some(map), _ => None, } } /// If the `Value` is a Map, returns a mutable reference to the associated Map Data. /// Returns None otherwise. /// /// ``` /// # use ciborium::Value; /// # /// let mut value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// value.as_map_mut().unwrap().clear(); /// assert_eq!(value, Value::Map(vec![])); /// assert_eq!(value.as_map().unwrap().len(), 0); /// ``` pub fn as_map_mut(&mut self) -> Option<&mut Vec<(Value, Value)>> { match *self { Value::Map(ref mut map) => Some(map), _ => 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, Self> { match self { Value::Map(map) => Ok(map), other => Err(other), } } } macro_rules! implfrom { ($($v:ident($t:ty)),+ $(,)?) => { $( impl From<$t> for Value { #[inline] fn from(value: $t) -> Self { Self::$v(value.into()) } } )+ }; } implfrom! { Integer(Integer), Integer(u64), Integer(i64), Integer(u32), Integer(i32), Integer(u16), Integer(i16), Integer(u8), Integer(i8), Bytes(Vec), Bytes(&[u8]), Float(f64), Float(f32), Text(String), Text(&str), Bool(bool), Array(&[Value]), Array(Vec), Map(&[(Value, Value)]), Map(Vec<(Value, Value)>), } impl From for Value { #[inline] fn from(value: u128) -> Self { if let Ok(x) = Integer::try_from(value) { return Value::Integer(x); } let mut bytes = &value.to_be_bytes()[..]; while let Some(0) = bytes.first() { bytes = &bytes[1..]; } Value::Tag(ciborium_ll::tag::BIGPOS, Value::Bytes(bytes.into()).into()) } } impl From for Value { #[inline] fn from(value: i128) -> Self { if let Ok(x) = Integer::try_from(value) { return Value::Integer(x); } let (tag, raw) = match value.is_negative() { true => (ciborium_ll::tag::BIGNEG, value as u128 ^ !0), false => (ciborium_ll::tag::BIGPOS, value as u128), }; let mut bytes = &raw.to_be_bytes()[..]; while let Some(0) = bytes.first() { bytes = &bytes[1..]; } Value::Tag(tag, Value::Bytes(bytes.into()).into()) } } impl From for Value { #[inline] fn from(value: char) -> Self { let mut v = String::with_capacity(1); v.push(value); Value::Text(v) } }