use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt;
use thiserror::Error;
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub(crate) enum ErrorKind {
#[error("badAlloc")]
BadAlloc,
#[error("truncated")]
Truncated,
#[error("invalidItemHeader")]
InvalidItemHeader,
#[error("largeIntegersUnsupported")]
LargeIntegersUnsupported,
#[error("integerOverflow")]
IntegerOverflow,
#[error("invalidChunkedString")]
InvalidChunkedString,
#[error("maxDepthExceeded")]
MaxDepthExceeded,
#[error("invalidSimpleEncoding")]
InvalidSimpleEncoding,
#[error("breakNotAllowed")]
BreakNotAllowed,
#[error("writeFailed")]
WriteFailed,
#[error("invalidSimpleValue")]
InvalidSimpleValue,
#[error("trailingData")]
TrailingData,
}
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[error("CBOR {kind} at byte {position}, depth {depth}")]
pub(crate) struct Error {
pub(crate) kind: ErrorKind,
pub(crate) position: usize,
pub(crate) depth: usize,
}
#[derive(Clone, Debug)]
pub(crate) enum Item<'a> {
Undefined,
Int(i64),
Bytes(Cow<'a, [u8]>),
Text(Cow<'a, [u8]>),
Array(Vec<Item<'a>>),
Map(Vec<(Item<'a>, Item<'a>)>),
Bool(bool),
Null,
Float16(u16),
Float32(f32),
Float64(f64),
Simple(u8),
Tag(u64, Box<Item<'a>>),
}
impl Default for Item<'_> {
fn default() -> Self {
Self::Undefined
}
}
impl PartialEq for Item<'_> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Undefined, Self::Undefined) | (Self::Null, Self::Null) => true,
(Self::Int(a), Self::Int(b)) => a == b,
(Self::Bytes(a), Self::Bytes(b)) | (Self::Text(a), Self::Text(b)) => a == b,
(Self::Array(a), Self::Array(b)) => a == b,
(Self::Map(a), Self::Map(b)) => a == b,
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::Float16(a), Self::Float16(b)) => a == b,
(Self::Float32(a), Self::Float32(b)) => a.to_bits() == b.to_bits(),
(Self::Float64(a), Self::Float64(b)) => a.to_bits() == b.to_bits(),
(Self::Simple(a), Self::Simple(b)) => a == b,
(Self::Tag(ta, a), Self::Tag(tb, b)) => ta == tb && a == b,
_ => false,
}
}
}
impl Eq for Item<'_> {}
impl<'a> Item<'a> {
pub(crate) fn text(text: &'a str) -> Self {
Self::Text(Cow::Borrowed(text.as_bytes()))
}
#[cfg(test)]
pub(crate) fn bytes(bytes: &'a [u8]) -> Self {
Self::Bytes(Cow::Borrowed(bytes))
}
pub(crate) fn map(pairs: impl IntoIterator<Item = (Item<'a>, Item<'a>)>) -> Self {
Self::Map(pairs.into_iter().collect())
}
pub(crate) fn array(items: impl IntoIterator<Item = Item<'a>>) -> Self {
Self::Array(items.into_iter().collect())
}
#[cfg(test)]
pub(crate) fn into_owned(self) -> Item<'static> {
match self {
Self::Undefined => Item::Undefined,
Self::Int(v) => Item::Int(v),
Self::Bytes(b) => Item::Bytes(Cow::Owned(b.into_owned())),
Self::Text(t) => Item::Text(Cow::Owned(t.into_owned())),
Self::Array(items) => Item::Array(items.into_iter().map(Item::into_owned).collect()),
Self::Map(pairs) => Item::Map(
pairs
.into_iter()
.map(|(k, v)| (k.into_owned(), v.into_owned()))
.collect(),
),
Self::Bool(v) => Item::Bool(v),
Self::Null => Item::Null,
Self::Float16(v) => Item::Float16(v),
Self::Float32(v) => Item::Float32(v),
Self::Float64(v) => Item::Float64(v),
Self::Simple(v) => Item::Simple(v),
Self::Tag(tag, inner) => Item::Tag(tag, Box::new(inner.into_owned())),
}
}
pub(crate) fn map_get(&self, key: &Item<'_>) -> Option<&Item<'a>> {
let Self::Map(pairs) = self else {
return None;
};
pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
pub(crate) fn map_get_str(&self, key: &str) -> Option<&Item<'a>> {
self.map_get(&Item::text(key))
}
#[cfg(test)]
pub(crate) fn map_get_int(&self, key: i64) -> Option<&Item<'a>> {
self.map_get(&Item::Int(key))
}
#[cfg(test)]
pub(crate) fn array_get(&self, index: usize) -> Option<&Item<'a>> {
match self {
Self::Array(items) => items.get(index),
_ => None,
}
}
pub(crate) const fn as_int64(&self) -> Option<i64> {
match *self {
Self::Int(v) => Some(v),
_ => None,
}
}
#[cfg(test)]
pub(crate) const fn as_bool(&self) -> Option<bool> {
match *self {
Self::Bool(v) => Some(v),
_ => None,
}
}
#[cfg(test)]
pub(crate) const fn as_float16(&self) -> Option<u16> {
match *self {
Self::Float16(v) => Some(v),
_ => None,
}
}
#[cfg(test)]
pub(crate) const fn as_float32(&self) -> Option<f32> {
match *self {
Self::Float32(v) => Some(v),
_ => None,
}
}
pub(crate) const fn as_float64(&self) -> Option<f64> {
match *self {
Self::Float64(v) => Some(v),
_ => None,
}
}
#[cfg(test)]
pub(crate) const fn as_simple(&self) -> Option<u8> {
match *self {
Self::Simple(v) => Some(v),
_ => None,
}
}
#[cfg(test)]
pub(crate) fn as_bytes(&self) -> Option<&[u8]> {
match self {
Self::Bytes(b) => Some(b),
_ => None,
}
}
pub(crate) fn as_text_bytes(&self) -> Option<&[u8]> {
match self {
Self::Text(t) => Some(t),
_ => None,
}
}
#[cfg(test)]
pub(crate) fn as_text(&self) -> Option<&str> {
core::str::from_utf8(self.as_text_bytes()?).ok()
}
pub(crate) fn as_array(&self) -> Option<&[Item<'a>]> {
match self {
Self::Array(items) => Some(items),
_ => None,
}
}
pub(crate) fn as_map(&self) -> Option<&[(Item<'a>, Item<'a>)]> {
match self {
Self::Map(pairs) => Some(pairs),
_ => None,
}
}
#[cfg(test)]
pub(crate) fn as_tag(&self) -> Option<(u64, &Item<'a>)> {
match self {
Self::Tag(tag, inner) => Some((*tag, inner)),
_ => None,
}
}
}
impl fmt::Display for Item<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Undefined => f.write_str("undefined"),
Self::Int(v) => write!(f, "{v}"),
Self::Bytes(b) => write!(f, "h'{}'", b.len()),
Self::Text(t) => write!(f, "\"{}\"", alloc::string::String::from_utf8_lossy(t)),
Self::Array(items) => {
f.write_str("[")?;
for (n, item) in items.iter().enumerate() {
if n != 0 {
f.write_str(", ")?;
}
write!(f, "{item}")?;
}
f.write_str("]")
}
Self::Map(pairs) => {
f.write_str("{")?;
for (n, (k, v)) in pairs.iter().enumerate() {
if n != 0 {
f.write_str(", ")?;
}
write!(f, "{k}: {v}")?;
}
f.write_str("}")
}
Self::Bool(v) => write!(f, "{v}"),
Self::Null => f.write_str("null"),
Self::Float16(v) => write!(f, "f16({v:#06x})"),
Self::Float32(v) => write!(f, "{v}f"),
Self::Float64(v) => write!(f, "{v}"),
Self::Simple(v) => write!(f, "simple({v})"),
Self::Tag(tag, inner) => write!(f, "{tag}({inner})"),
}
}
}