import_stdlib!();
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(all(not(feature = "multithreaded"), not(feature = "std")))]
use alloc::rc::Rc as RefCounted;
#[cfg(all(feature = "multithreaded", not(feature = "std")))]
use alloc::sync::Arc as RefCounted;
#[cfg(all(not(feature = "multithreaded"), feature = "std"))]
use std::rc::Rc as RefCounted;
#[cfg(all(feature = "multithreaded", feature = "std"))]
use std::sync::Arc as RefCounted;
use unicode_normalization::UnicodeNormalization;
use super::string_util::flanked;
use crate::{
ByteString, Map, Simple,
decode::decode_cbor,
error::Result,
tag::Tag,
varint::{EncodeVarInt, MajorType},
};
#[derive(Clone, Eq)]
pub struct CBOR(RefCounted<CBORCase>);
impl CBOR {
pub fn as_case(&self) -> &CBORCase { &self.0 }
pub fn into_case(self) -> CBORCase {
match RefCounted::try_unwrap(self.0) {
Ok(b) => b,
Err(ref_counted) => (*ref_counted).clone(),
}
}
}
impl From<CBORCase> for CBOR {
fn from(case: CBORCase) -> Self { Self(RefCounted::new(case)) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CBORCase {
Unsigned(u64),
Negative(u64),
ByteString(ByteString),
Text(String),
Array(Vec<CBOR>),
Map(Map),
Tagged(Tag, CBOR),
Simple(Simple),
}
impl CBOR {
pub fn try_from_data(data: impl AsRef<[u8]>) -> Result<CBOR> {
decode_cbor(data)
}
pub fn try_from_hex(hex: &str) -> Result<CBOR> {
let data = hex::decode(hex).unwrap();
Self::try_from_data(data)
}
pub fn to_cbor_data(&self) -> Vec<u8> {
match self.as_case() {
CBORCase::Unsigned(x) => x.encode_varint(MajorType::Unsigned),
CBORCase::Negative(x) => x.encode_varint(MajorType::Negative),
CBORCase::ByteString(x) => {
let mut buf = x.len().encode_varint(MajorType::ByteString);
buf.extend(x);
buf
}
CBORCase::Text(x) => {
let nfc = x.nfc().collect::<String>();
let mut buf = nfc.len().encode_varint(MajorType::Text);
buf.extend(nfc.as_bytes());
buf
}
CBORCase::Array(x) => {
let mut buf = x.len().encode_varint(MajorType::Array);
for item in x {
buf.extend(item.to_cbor_data());
}
buf
}
CBORCase::Map(x) => x.cbor_data(),
CBORCase::Tagged(tag, item) => {
let mut buf = tag.value().encode_varint(MajorType::Tagged);
buf.extend(item.to_cbor_data());
buf
}
CBORCase::Simple(x) => x.cbor_data(),
}
}
}
impl PartialEq for CBOR {
fn eq(&self, other: &Self) -> bool {
match (self.as_case(), other.as_case()) {
(CBORCase::Unsigned(l0), CBORCase::Unsigned(r0)) => l0 == r0,
(CBORCase::Negative(l0), CBORCase::Negative(r0)) => l0 == r0,
(CBORCase::ByteString(l0), CBORCase::ByteString(r0)) => l0 == r0,
(CBORCase::Text(l0), CBORCase::Text(r0)) => l0 == r0,
(CBORCase::Array(l0), CBORCase::Array(r0)) => l0 == r0,
(CBORCase::Map(l0), CBORCase::Map(r0)) => l0 == r0,
(CBORCase::Tagged(l0, l1), CBORCase::Tagged(r0, r1)) => {
l0 == r0 && l1 == r1
}
(CBORCase::Simple(l0), CBORCase::Simple(r0)) => l0 == r0,
_ => false,
}
}
}
impl hash::Hash for CBOR {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
use CBORCase::*;
match self.as_case() {
Unsigned(x) => {
0u8.hash(state);
x.hash(state);
}
Negative(x) => {
1u8.hash(state);
x.hash(state);
}
ByteString(x) => {
2u8.hash(state);
x.hash(state);
}
Text(x) => {
3u8.hash(state);
x.hash(state);
}
Array(x) => {
4u8.hash(state);
x.hash(state);
}
Map(x) => {
5u8.hash(state);
x.hash(state);
}
Tagged(tag, item) => {
6u8.hash(state);
tag.hash(state);
item.hash(state);
}
Simple(x) => {
7u8.hash(state);
x.hash(state);
}
}
}
}
fn format_string(s: &str) -> String {
let mut result = "".to_string();
for c in s.chars() {
if c == '"' {
result.push_str(r#"\""#);
} else {
result.push(c);
}
}
flanked(&result, r#"""#, r#"""#)
}
fn format_array(a: &[CBOR]) -> String {
let s: Vec<String> = a.iter().map(|x| format!("{}", x)).collect();
flanked(&s.join(", "), "[", "]")
}
fn format_map(m: &Map) -> String {
let s: Vec<String> =
m.iter().map(|x| format!("{}: {}", x.0, x.1)).collect();
flanked(&s.join(", "), "{", "}")
}
impl fmt::Debug for CBOR {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.as_case() {
CBORCase::Unsigned(x) => {
f.debug_tuple("unsigned").field(x).finish()
}
CBORCase::Negative(x) => f
.debug_tuple("negative")
.field(&(-1 - (*x as i128)))
.finish(),
CBORCase::ByteString(x) => {
f.write_fmt(format_args!("bytes({})", hex::encode(x)))
}
CBORCase::Text(x) => f.debug_tuple("text").field(x).finish(),
CBORCase::Array(x) => f.debug_tuple("array").field(x).finish(),
CBORCase::Map(x) => f.debug_tuple("map").field(x).finish(),
CBORCase::Tagged(tag, item) => {
f.write_fmt(format_args!("tagged({}, {:?})", tag, item))
}
CBORCase::Simple(x) => {
f.write_fmt(format_args!("simple({})", x.name()))
}
}
}
}
impl fmt::Display for CBOR {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self.as_case() {
CBORCase::Unsigned(x) => format!("{}", x),
CBORCase::Negative(x) => format!("{}", -1 - (*x as i128)),
CBORCase::ByteString(x) => format!("h'{}'", hex::encode(x)),
CBORCase::Text(x) => format_string(x),
CBORCase::Array(x) => format_array(x),
CBORCase::Map(x) => format_map(x),
CBORCase::Tagged(tag, item) => format!("{}({})", tag, item),
CBORCase::Simple(x) => format!("{}", x),
};
f.write_str(&s)
}
}