mod array;
mod bytes;
mod debug;
mod eq_ord_hash;
mod float;
mod index;
mod int;
mod map;
mod simple_value;
mod string;
use std::{
borrow::Cow,
cmp,
collections::BTreeMap,
hash::{Hash, Hasher},
time::{Duration, SystemTime},
};
use crate::{
Array, ByteString, DataType, DateTime, EpochTime, Error, Float, IntegerBytes, Map, Result, SimpleValue, TextString,
codec::{Head, Major},
tag,
util::u128_from_slice,
view::{Payload, ValueView},
};
#[derive(Clone)]
pub enum Value<'a> {
SimpleValue(SimpleValue),
Unsigned(u64),
Negative(u64),
Float(Float),
ByteString(Cow<'a, [u8]>),
TextString(Cow<'a, str>),
Array(Vec<Value<'a>>),
Map(BTreeMap<Value<'a>, Value<'a>>),
Tag(u64, Box<Value<'a>>),
}
impl<'a> Default for Value<'a> {
fn default() -> Self {
Self::null()
}
}
impl<'a> From<()> for Value<'a> {
fn from(_: ()) -> Self {
Value::null()
}
}
impl<'a> Value<'a> {
#[must_use]
pub const fn null() -> Self {
Self::SimpleValue(SimpleValue::NULL)
}
#[must_use]
pub const fn simple_value(value: u8) -> Self {
match SimpleValue::from_u8(value) {
Ok(sv) => Self::SimpleValue(sv),
Err(_) => panic!("Invalid simple value"),
}
}
#[must_use]
pub const fn from_bool(value: bool) -> Self {
Self::SimpleValue(SimpleValue::from_bool(value))
}
#[must_use]
pub const fn from_u64(value: u64) -> Self {
Self::Unsigned(value)
}
#[must_use]
pub const fn from_i64(value: i64) -> Self {
if value >= 0 {
Self::Unsigned(value as u64)
} else {
Self::Negative((!value) as u64)
}
}
#[must_use]
pub const fn from_f32(value: f32) -> Self {
Self::Float(Float::from_f32(value))
}
#[must_use]
pub const fn from_f64(value: f64) -> Self {
Self::Float(Float::from_f64(value))
}
#[must_use]
pub const fn from_payload(payload: u64) -> Self {
Self::Float(Float::with_payload(payload))
}
#[must_use]
pub const fn from_str_slice(s: &'a str) -> Self {
Self::TextString(Cow::Borrowed(s))
}
#[must_use]
pub const fn from_byte_slice(b: &'a [u8]) -> Self {
Self::ByteString(Cow::Borrowed(b))
}
#[must_use]
pub fn new(value: impl TryInto<Value<'a>>) -> Self {
match value.try_into() {
Ok(value) => value,
Err(_) => panic!("Invalid CBOR value"),
}
}
#[must_use]
pub fn byte_string(value: impl Into<ByteString<'a>>) -> Self {
Value::from(value.into())
}
#[must_use]
pub fn text_string(value: impl Into<TextString<'a>>) -> Self {
Self::from(value.into())
}
#[must_use]
pub fn date_time(value: impl TryInto<DateTime>) -> Self {
match value.try_into() {
Ok(dt) => dt.into(),
Err(_) => panic!("Invalid date/time"),
}
}
#[must_use]
pub fn epoch_time(value: impl TryInto<EpochTime>) -> Self {
match value.try_into() {
Ok(et) => et.into(),
Err(_) => panic!("Invalid epoch time"),
}
}
#[must_use]
pub fn float(value: impl Into<Float>) -> Self {
Self::Float(value.into())
}
#[must_use]
pub fn array(array: impl Into<Array<'a>>) -> Self {
Self::Array(array.into().0)
}
#[must_use]
pub fn map(map: impl Into<Map<'a>>) -> Self {
Self::Map(map.into().0)
}
#[must_use]
pub fn tag(number: u64, content: impl Into<Value<'a>>) -> Self {
Self::Tag(number, Box::new(content.into()))
}
pub fn to_owned<'b>(&self) -> Value<'b> {
match self {
Self::SimpleValue(simple_value) => Value::SimpleValue(*simple_value),
Self::Unsigned(x) => Value::Unsigned(*x),
Self::Negative(x) => Value::Negative(*x),
Self::Float(float) => Value::Float(*float),
Self::ByteString(text) => Value::ByteString(text.clone().into_owned().into()),
Self::TextString(bytes) => Value::TextString(bytes.clone().into_owned().into()),
Self::Array(values) => Value::Array(values.iter().map(Value::to_owned).collect()),
Self::Map(map) => Value::Map(map.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect()),
Self::Tag(tag, content) => Value::Tag(*tag, Box::new((**content).to_owned())),
}
}
pub fn into_owned<'b>(self) -> Value<'b> {
match self {
Self::SimpleValue(simple_value) => Value::SimpleValue(simple_value),
Self::Unsigned(x) => Value::Unsigned(x),
Self::Negative(x) => Value::Negative(x),
Self::Float(float) => Value::Float(float),
Self::ByteString(text) => Value::ByteString(text.into_owned().into()),
Self::TextString(bytes) => Value::TextString(bytes.into_owned().into()),
Self::Array(values) => Value::Array(values.into_iter().map(Value::into_owned).collect()),
Self::Map(map) => Value::Map(map.into_iter().map(|(k, v)| (k.into_owned(), v.into_owned())).collect()),
Self::Tag(tag, content) => Value::Tag(tag, Box::new(content.into_owned())), }
}
}
impl<'a> Value<'a> {
pub fn decode<T>(bytes: &'a T) -> crate::Result<Self>
where
T: AsRef<[u8]> + ?Sized,
{
crate::DecodeOptions::new().decode(bytes)
}
pub fn decode_owned(bytes: impl AsRef<[u8]>) -> crate::Result<Self> {
crate::DecodeOptions::new().decode_owned(bytes)
}
pub fn decode_hex(hex: impl AsRef<[u8]>) -> crate::Result<Self> {
crate::DecodeOptions::new().format(crate::Format::Hex).decode_owned(hex)
}
pub fn read_from(reader: impl std::io::Read) -> crate::IoResult<Self> {
crate::DecodeOptions::new().read_from(reader)
}
pub fn read_hex_from(reader: impl std::io::Read) -> crate::IoResult<Self> {
crate::DecodeOptions::new().format(crate::Format::Hex).read_from(reader)
}
}
impl<'a> Value<'a> {
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let len = self.encoded_len();
let mut bytes = Vec::with_capacity(len);
self.write_to(&mut bytes).unwrap();
debug_assert_eq!(bytes.len(), len);
bytes
}
#[must_use]
pub fn encode_hex(&self) -> String {
let len2 = self.encoded_len() * 2;
let mut hex = Vec::with_capacity(len2);
self.write_hex_to(&mut hex).unwrap();
debug_assert_eq!(hex.len(), len2);
String::from_utf8(hex).unwrap()
}
pub fn write_to(&self, mut writer: impl std::io::Write) -> std::io::Result<()> {
self.do_write(&mut writer)
}
pub fn write_hex_to(&self, writer: impl std::io::Write) -> std::io::Result<()> {
struct HexWriter<W>(W);
impl<W: std::io::Write> std::io::Write for HexWriter<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
for &byte in buf {
write!(self.0, "{byte:02x}")?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
self.do_write(&mut HexWriter(writer))
}
fn do_write(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
self.head().write_to(writer)?;
match self {
Value::ByteString(bytes) => writer.write_all(bytes)?,
Value::TextString(string) => writer.write_all(string.as_bytes())?,
Value::Tag(_number, content) => content.do_write(writer)?,
Value::Array(values) => {
for value in values {
value.do_write(writer)?;
}
}
Value::Map(map) => {
for (key, value) in map {
key.do_write(writer)?;
value.do_write(writer)?;
}
}
_ => (),
}
Ok(())
}
pub(crate) fn encoded_len(&self) -> usize {
self.head().encoded_len() + self.payload().encoded_len()
}
}
impl<'a> ValueView for Value<'a> {
fn head(&self) -> Head {
match self {
Value::SimpleValue(sv) => Head::from_u64(Major::SimpleOrFloat, sv.0.into()),
Value::Unsigned(n) => Head::from_u64(Major::Unsigned, *n),
Value::Negative(n) => Head::from_u64(Major::Negative, *n),
Value::Float(float) => float.head(),
Value::ByteString(bytes) => Head::from_usize(Major::ByteString, bytes.len()),
Value::TextString(text) => Head::from_usize(Major::TextString, text.len()),
Value::Array(vec) => Head::from_usize(Major::Array, vec.len()),
Value::Map(map) => Head::from_usize(Major::Map, map.len()),
Value::Tag(number, _content) => Head::from_u64(Major::Tag, *number),
}
}
fn payload(&self) -> Payload<'_> {
match self {
Value::SimpleValue(_) | Value::Unsigned(_) | Value::Negative(_) | Value::Float(_) => Payload::None,
Value::ByteString(bytes) => Payload::Bytes(bytes),
Value::TextString(text) => Payload::Text(text),
Value::Array(arr) => Payload::Array(arr),
Value::Map(map) => Payload::Map(map),
Value::Tag(_, content) => Payload::TagContent(content),
}
}
}
impl<'a> Value<'a> {
#[must_use]
pub const fn data_type(&self) -> DataType {
match self {
Self::SimpleValue(sv) => sv.data_type(),
Self::Unsigned(_) | Self::Negative(_) => DataType::Int,
Self::Float(float) => float.data_type(),
Self::TextString(_) => DataType::Text,
Self::ByteString(_) => DataType::Bytes,
Self::Array(_) => DataType::Array,
Self::Map(_) => DataType::Map,
Self::Tag(tag::DATE_TIME, content) if content.data_type().is_text() => DataType::DateTime,
Self::Tag(tag::EPOCH_TIME, content) if content.data_type().is_numeric() => DataType::EpochTime,
Self::Tag(tag::POS_BIG_INT | tag::NEG_BIG_INT, content) if content.data_type().is_bytes() => {
DataType::BigInt
}
Self::Tag(_, _) => DataType::Tag,
}
}
const fn is_bytes(&self) -> bool {
self.data_type().is_bytes()
}
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub fn replace(&mut self, value: Self) -> Self {
std::mem::replace(self, value)
}
}
impl<'a> Value<'a> {
pub const fn to_bool(&self) -> Result<bool> {
match self {
Self::SimpleValue(sv) => sv.to_bool(),
Self::Tag(_number, content) => content.untagged().to_bool(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn to_simple_value(&self) -> Result<u8> {
match self {
Self::SimpleValue(sv) => Ok(sv.0),
Self::Tag(_number, content) => content.untagged().to_simple_value(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
fn to_uint<T>(&self) -> Result<T>
where
T: TryFrom<u64> + TryFrom<u128>,
{
match self {
Self::Unsigned(x) => T::try_from(*x).or(Err(Error::Overflow)),
Self::Negative(_) => Err(Error::NegativeUnsigned),
Self::Tag(tag::POS_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?).or(Err(Error::Overflow))
}
Self::Tag(tag::NEG_BIG_INT, content) if content.is_bytes() => Err(Error::NegativeUnsigned),
Self::Tag(_other_number, content) => content.peeled().to_uint(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_u8(&self) -> Result<u8> {
self.to_uint()
}
pub fn to_u16(&self) -> Result<u16> {
self.to_uint()
}
pub fn to_u32(&self) -> Result<u32> {
self.to_uint()
}
pub fn to_u64(&self) -> Result<u64> {
self.to_uint()
}
pub fn to_u128(&self) -> Result<u128> {
self.to_uint()
}
pub fn to_usize(&self) -> Result<usize> {
self.to_uint()
}
#[allow(dead_code)]
pub(crate) fn as_integer_bytes(&self) -> Result<IntegerBytes<'_>> {
match self {
Self::Unsigned(x) => Ok(IntegerBytes::UnsignedOwned(x.to_be_bytes())),
Self::Negative(x) => Ok(IntegerBytes::NegativeOwned(x.to_be_bytes())),
Self::Tag(tag::POS_BIG_INT, content) if content.is_bytes() => {
Ok(IntegerBytes::UnsignedBorrowed(content.as_bytes()?))
}
Self::Tag(tag::NEG_BIG_INT, content) if content.is_bytes() => {
Ok(IntegerBytes::NegativeBorrowed(content.as_bytes()?))
}
Self::Tag(_other_number, content) => content.peeled().as_integer_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
fn to_sint<T>(&self) -> Result<T>
where
T: TryFrom<u64> + TryFrom<u128> + std::ops::Not<Output = T>,
{
match self {
Self::Unsigned(x) => T::try_from(*x).or(Err(Error::Overflow)),
Self::Negative(x) => T::try_from(*x).map(T::not).or(Err(Error::Overflow)),
Self::Tag(tag::POS_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?).or(Err(Error::Overflow))
}
Self::Tag(tag::NEG_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?)
.map(T::not)
.or(Err(Error::Overflow))
}
Self::Tag(_other_number, content) => content.peeled().to_sint(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_i8(&self) -> Result<i8> {
self.to_sint()
}
pub fn to_i16(&self) -> Result<i16> {
self.to_sint()
}
pub fn to_i32(&self) -> Result<i32> {
self.to_sint()
}
pub fn to_i64(&self) -> Result<i64> {
self.to_sint()
}
pub fn to_i128(&self) -> Result<i128> {
self.to_sint()
}
pub fn to_isize(&self) -> Result<isize> {
self.to_sint()
}
pub fn to_f32(&self) -> Result<f32> {
match self {
Self::Float(float) => float.to_f32(),
Self::Tag(_number, content) => content.untagged().to_f32(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_f64(&self) -> Result<f64> {
match self {
Self::Float(float) => Ok(float.to_f64()),
Self::Tag(_number, content) => content.untagged().to_f64(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_system_time(&self) -> Result<SystemTime> {
if let Ok(s) = self.as_str() {
Ok(s.parse::<crate::iso3339::Timestamp>()?.try_into()?)
} else if let Ok(f) = self.to_f64() {
if f.is_finite() && (0.0..=253402300799.0).contains(&f) {
Ok(SystemTime::UNIX_EPOCH + Duration::from_secs_f64(f))
} else {
Err(Error::InvalidValue)
}
} else {
match self.to_u64() {
Ok(secs) if secs <= 253402300799 => Ok(SystemTime::UNIX_EPOCH + Duration::from_secs(secs)),
Ok(_) | Err(Error::NegativeUnsigned) => Err(Error::InvalidValue),
Err(error) => Err(error),
}
}
}
}
impl<'a> Value<'a> {
pub fn as_bytes(&self) -> Result<&[u8]> {
match self {
Self::ByteString(bytes) => Ok(bytes.as_ref()),
Self::Tag(_number, content) => content.untagged().as_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_bytes_mut(&mut self) -> Result<&mut Vec<u8>> {
match self {
Self::ByteString(bytes) => Ok(bytes.to_mut()),
Self::Tag(_number, content) => content.untagged_mut().as_bytes_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_bytes(self) -> Result<Vec<u8>> {
match self {
Self::ByteString(bytes) => Ok(bytes.into_owned()),
Self::Tag(_number, content) => content.into_untagged().into_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_str(&self) -> Result<&str> {
match self {
Self::TextString(s) => Ok(s.as_ref()),
Self::Tag(_number, content) => content.untagged().as_str(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_string_mut(&mut self) -> Result<&mut String> {
match self {
Self::TextString(s) => Ok(s.to_mut()),
Self::Tag(_number, content) => content.untagged_mut().as_string_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_string(self) -> Result<String> {
match self {
Self::TextString(s) => Ok(s.into_owned()),
Self::Tag(_number, content) => content.into_untagged().into_string(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
}
impl<'a> Value<'a> {
pub fn as_array(&self) -> Result<&[Value<'a>]> {
match self {
Self::Array(v) => Ok(v.as_slice()),
Self::Tag(_number, content) => content.untagged().as_array(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_array_mut(&mut self) -> Result<&mut Vec<Value<'a>>> {
match self {
Self::Array(v) => Ok(v),
Self::Tag(_number, content) => content.untagged_mut().as_array_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_array(self) -> Result<Vec<Value<'a>>> {
match self {
Self::Array(v) => Ok(v),
Self::Tag(_number, content) => content.into_untagged().into_array(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_map(&self) -> Result<&BTreeMap<Value<'a>, Value<'a>>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.untagged().as_map(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_map_mut(&mut self) -> Result<&mut BTreeMap<Value<'a>, Value<'a>>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.untagged_mut().as_map_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_map(self) -> Result<BTreeMap<Value<'a>, Value<'a>>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.into_untagged().into_map(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
}
impl<'a> Value<'a> {
pub fn get<'k>(&self, index: impl Into<crate::ValueKey<'k>>) -> Option<&Value<'a>> {
let key = index.into();
match self.untagged() {
Value::Array(arr) => key.to_usize().and_then(|idx| arr.get(idx)),
Value::Map(map) => map.get(&key as &dyn ValueView),
_ => None,
}
}
pub fn get_mut<'k>(&mut self, index: impl Into<crate::ValueKey<'k>>) -> Option<&mut Value<'a>> {
let key = index.into();
match self.untagged_mut() {
Value::Array(arr) => key.to_usize().and_then(|idx| arr.get_mut(idx)),
Value::Map(map) => map.get_mut(&key as &dyn ValueView),
_ => None,
}
}
pub fn remove<'k>(&mut self, index: impl Into<crate::ValueKey<'k>>) -> Option<Value<'a>> {
let key = index.into();
match self.untagged_mut() {
Value::Array(arr) => {
let idx = key.to_usize().expect("array index must be a non-negative integer");
assert!(idx < arr.len(), "array index {idx} out of bounds (len {})", arr.len());
Some(arr.remove(idx))
}
Value::Map(map) => map.remove(&key as &dyn ValueView),
other => panic!("remove called on {:?}, expected array or map", other.data_type()),
}
}
pub fn insert(&mut self, key: impl Into<Value<'a>>, value: impl Into<Value<'a>>) -> Option<Value<'a>> {
let key = key.into();
let value = value.into();
match self.untagged_mut() {
Value::Array(arr) => {
let idx = key.to_usize().expect("array index must be a non-negative integer");
assert!(idx <= arr.len(), "array index {idx} out of bounds (len {})", arr.len());
arr.insert(idx, value);
None
}
Value::Map(map) => map.insert(key, value),
other => panic!("insert called on {:?}, expected array or map", other.data_type()),
}
}
pub fn append(&mut self, value: impl Into<Value<'a>>) {
match self.untagged_mut() {
Value::Array(arr) => arr.push(value.into()),
other => panic!("append called on {:?}, expected array", other.data_type()),
}
}
pub fn contains<'k>(&self, key: impl Into<crate::ValueKey<'k>>) -> bool {
let key = key.into();
match self.untagged() {
Value::Array(arr) => key.to_usize().is_some_and(|idx| idx < arr.len()),
Value::Map(map) => map.contains_key(&key as &dyn ValueView),
_ => false,
}
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> Option<usize> {
match self.untagged() {
Value::Array(arr) => Some(arr.len()),
Value::Map(map) => Some(map.len()),
_ => None,
}
}
}
impl<'a> Value<'a> {
pub const fn tag_number(&self) -> Result<u64> {
match self {
Self::Tag(number, _content) => Ok(*number),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn tag_content(&self) -> Result<&Self> {
match self {
Self::Tag(_tag, content) => Ok(content),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn tag_content_mut(&mut self) -> Result<&mut Self> {
match self {
Self::Tag(_, value) => Ok(value),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_tag(&self) -> Result<(u64, &Value<'a>)> {
match self {
Self::Tag(number, content) => Ok((*number, content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_tag_mut(&mut self) -> Result<(u64, &mut Value<'a>)> {
match self {
Self::Tag(number, content) => Ok((*number, content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_tag(self) -> Result<(u64, Value<'a>)> {
match self {
Self::Tag(number, content) => Ok((number, *content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn remove_tag(&mut self) -> Option<u64> {
let mut result = None;
if let Self::Tag(number, content) = self {
result = Some(*number);
*self = std::mem::take(content);
}
result
}
pub fn remove_all_tags(&mut self) -> Vec<u64> {
let mut tags = Vec::new();
while let Self::Tag(number, content) = self {
tags.push(*number);
*self = std::mem::take(content);
}
tags
}
#[must_use]
pub(crate) const fn peeled(&self) -> &Self {
let mut result = self;
while let Self::Tag(_, content) = result
&& content.data_type().is_tag()
{
result = content;
}
result
}
#[must_use]
pub const fn untagged(&self) -> &Self {
let mut result = self;
while let Self::Tag(_, content) = result {
result = content;
}
result
}
pub const fn untagged_mut(&mut self) -> &mut Self {
let mut result = self;
while let Self::Tag(_, content) = result {
result = content;
}
result
}
#[must_use]
pub fn into_untagged(mut self) -> Self {
while let Self::Tag(_number, content) = self {
self = *content;
}
self
}
}