#![doc = include_str!("../README.md")]
use std::{
borrow::{Borrow, Cow},
collections::BTreeMap,
convert::TryFrom,
fmt::{Debug, Display, Write},
ops::Deref,
};
mod builder;
mod canonical;
mod check;
pub mod codec;
pub mod constants;
mod error;
mod reader;
mod validated;
pub mod value;
mod visit;
#[cfg(test)]
mod tests;
pub use builder::{
ArrayWriter, CborBuilder, CborOutput, DictWriter, Encoder, KeyBuilder, NoOutput, SingleBuilder,
SingleResult, WithOutput, Writer,
};
pub use error::{ErrorKind, ParseError, WhileParsing};
pub use reader::Literal;
pub use validated::{
indexing::{IndexStr, PathElement},
item::{ItemKind, ItemKindShort, TaggedItem},
iterators::{ArrayIter, BytesIter, DictIter, StringIter},
tags::{Tags, TagsShort},
};
pub use value::CborValue;
pub use visit::Visitor;
use canonical::canonicalise;
use smallvec::SmallVec;
use validated::indexing::IndexVisitor;
use visit::visit;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Cbor([u8]);
impl From<&Cbor> for SmallVec<[u8; 16]> {
fn from(a: &Cbor) -> Self {
(&a.0).into()
}
}
impl Debug for Cbor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut groups = 0;
f.write_str("Cbor(")?;
if f.alternate() {
for chunk in self.0.chunks(4) {
let c = if groups & 15 == 0 { '\n' } else { ' ' };
f.write_char(c)?;
groups += 1;
for byte in chunk {
write!(f, "{:02x}", byte)?;
}
}
f.write_char('\n')?;
} else {
for chunk in self.0.chunks(4) {
if groups > 0 {
f.write_char(' ')?;
} else {
groups = 1;
}
for byte in chunk {
write!(f, "{:02x}", byte)?;
}
}
}
f.write_char(')')
}
}
impl Display for Cbor {
fn fmt(&self, mut f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<'a> Visitor<'a, std::fmt::Error> for &mut std::fmt::Formatter<'_> {
fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
write!(self, "{}", item)?;
Ok(())
}
fn visit_array_begin(
&mut self,
array: TaggedItem<'a>,
size: Option<u64>,
) -> Result<bool, std::fmt::Error> {
for tag in array.tags() {
write!(self, "{}(", tag)?;
}
write!(self, "[")?;
if size.is_none() {
write!(self, "_ ")?;
}
Ok(true)
}
fn visit_array_index(
&mut self,
_array: TaggedItem<'a>,
index: u64,
) -> Result<bool, std::fmt::Error> {
if index > 0 {
write!(self, ", ")?;
}
Ok(true)
}
fn visit_array_end(&mut self, array: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
write!(self, "]")?;
for _ in array.tags() {
write!(self, ")")?;
}
Ok(())
}
fn visit_dict_begin(
&mut self,
dict: TaggedItem<'a>,
size: Option<u64>,
) -> Result<bool, std::fmt::Error> {
for tag in dict.tags() {
write!(self, "{}(", tag)?;
}
write!(self, "{{")?;
if size.is_none() {
write!(self, "_ ")?;
}
Ok(true)
}
fn visit_dict_key(
&mut self,
_dict: TaggedItem<'a>,
key: TaggedItem<'a>,
is_first: bool,
) -> Result<bool, std::fmt::Error> {
if !is_first {
write!(self, ", ")?;
}
write!(self, "{}: ", key)?;
Ok(true)
}
fn visit_dict_end(&mut self, dict: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
write!(self, "}}")?;
for _ in dict.tags() {
write!(self, ")")?;
}
Ok(())
}
}
visit(&mut f, self.tagged_item())
}
}
impl AsRef<[u8]> for Cbor {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<'a> TryFrom<&'a [u8]> for &'a Cbor {
type Error = error::ParseError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Cbor::checked(value)
}
}
impl ToOwned for Cbor {
type Owned = CborOwned;
fn to_owned(&self) -> Self::Owned {
CborOwned::unchecked(&self.0)
}
}
impl Cbor {
pub fn unchecked(bytes: &[u8]) -> &Self {
unsafe { std::mem::transmute(bytes) }
}
pub fn from_cow_unchecked(bytes: Cow<'_, [u8]>) -> Cow<'_, Cbor> {
match bytes {
Cow::Borrowed(b) => Cow::Borrowed(Cbor::unchecked(b)),
Cow::Owned(v) => Cow::Owned(CborOwned::unchecked(v)),
}
}
pub fn checked(bytes: &[u8]) -> Result<&Self, ParseError> {
check::validate(bytes, false).map(|(cbor, _rest)| cbor)
}
pub fn checked_prefix(bytes: &[u8]) -> Result<(&Self, &[u8]), ParseError> {
check::validate(bytes, true)
}
pub fn from_cow_checked(bytes: Cow<'_, [u8]>) -> Result<Cow<'_, Cbor>, ParseError> {
match bytes {
Cow::Borrowed(b) => Cbor::checked(b).map(Cow::Borrowed),
Cow::Owned(v) => CborOwned::canonical(v).map(Cow::Owned),
}
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn decode(&self) -> CborValue<'_> {
CborValue::new(self.tagged_item())
}
pub fn tags(&self) -> Tags<'_> {
reader::tags(self.as_slice()).unwrap().0
}
pub fn kind(&self) -> ItemKind<'_> {
ItemKind::new(self)
}
pub fn tagged_item(&self) -> TaggedItem<'_> {
TaggedItem::new(self)
}
pub fn try_null(&self) -> Result<(), TypeError> {
let item = self.tagged_item();
if CborValue::new(item).is_null() {
Ok(())
} else {
Err(TypeError {
target: "null",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
}
pub fn try_bool(&self) -> Result<bool, TypeError> {
let item = self.tagged_item();
CborValue::new(item).as_bool().ok_or(TypeError {
target: "boolean",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_number(&self) -> Result<value::Number, TypeError> {
let item = self.tagged_item();
CborValue::new(item).to_number().ok_or(TypeError {
target: "number",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_timestamp(&self) -> Result<value::Timestamp, TypeError> {
let item = self.tagged_item();
CborValue::new(item).as_timestamp().ok_or(TypeError {
target: "timestamp",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_bytes(&self) -> Result<Cow<[u8]>, TypeError> {
let item = self.tagged_item();
CborValue::new(item).to_bytes().ok_or(TypeError {
target: "byte string",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_str(&self) -> Result<Cow<str>, TypeError> {
let item = self.tagged_item();
CborValue::new(item).to_str().ok_or(TypeError {
target: "string",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_array(&self) -> Result<Vec<Cow<Cbor>>, TypeError> {
let item = self.tagged_item();
CborValue::new(item).to_array().ok_or(TypeError {
target: "array",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn try_dict(&self) -> Result<BTreeMap<Cow<Cbor>, Cow<Cbor>>, TypeError> {
let item = self.tagged_item();
CborValue::new(item).to_dict().ok_or(TypeError {
target: "dictionary",
kind: item.kind().into(),
tags: item.tags().into(),
})
}
pub fn index<'a, 'b>(
&'a self,
path: impl IntoIterator<Item = PathElement<'b>>,
) -> Option<Cow<'a, Cbor>> {
visit(&mut IndexVisitor::new(path.into_iter()), self.tagged_item()).unwrap_err()
}
pub fn index_borrowed<'a, 'b>(
&'a self,
path: impl IntoIterator<Item = PathElement<'b>>,
) -> Option<&'a Cbor> {
self.index(path).map(|cow| match cow {
Cow::Borrowed(b) => b,
Cow::Owned(_) => panic!("indexing required allocation"),
})
}
pub fn visit<'a, 'b, Err, V: Visitor<'a, Err> + 'b>(
&'a self,
visitor: &'b mut V,
) -> Result<(), Err> {
visit(visitor, self.tagged_item())
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct TypeError {
target: &'static str,
kind: ItemKindShort,
tags: TagsShort,
}
impl Display for TypeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"type error when reading {}: found {} (tags: {:?})",
self.target, self.kind, self.tags
)
}
}
impl std::error::Error for TypeError {}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct CborOwned(SmallVec<[u8; 16]>);
impl Debug for CborOwned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(Borrow::<Cbor>::borrow(self), f)
}
}
impl Display for CborOwned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(Borrow::<Cbor>::borrow(self), f)
}
}
impl Borrow<Cbor> for CborOwned {
fn borrow(&self) -> &Cbor {
Cbor::unchecked(&*self.0)
}
}
impl AsRef<Cbor> for CborOwned {
fn as_ref(&self) -> &Cbor {
Cbor::unchecked(&*self.0)
}
}
impl AsRef<[u8]> for CborOwned {
fn as_ref(&self) -> &[u8] {
&*self.0
}
}
impl Deref for CborOwned {
type Target = Cbor;
fn deref(&self) -> &Self::Target {
self.borrow()
}
}
impl TryFrom<&[u8]> for CborOwned {
type Error = error::ParseError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Self::canonical(value)
}
}
impl CborOwned {
pub fn unchecked(bytes: impl Into<SmallVec<[u8; 16]>>) -> Self {
Self(bytes.into())
}
pub fn canonical(bytes: impl AsRef<[u8]>) -> Result<Self, ParseError> {
canonicalise(bytes.as_ref(), CborBuilder::new())
}
pub fn into_vec(self) -> Vec<u8> {
self.0.into_vec()
}
}
pub fn try_index_str(s: &str) -> Option<IndexStr<'_>> {
IndexStr::new(s)
}
pub fn index_str(s: &str) -> IndexStr<'_> {
try_index_str(s).expect("invalid index string")
}
struct DebugUsingDisplay<'a, T>(&'a T);
impl<'a, T: Display> Debug for DebugUsingDisplay<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.0, f)
}
}