#![forbid(unsafe_code)]
use std::borrow::Cow;
mod visitor;
use visitor::{
ApplicationLiteralsVisitor, ArrayElementVisitor, MapElementVisitor, MapValueHandler,
ProcessResult, TagVisitor, Visitor,
};
pub mod application;
pub mod error;
mod float;
mod space;
use space::{Comment, SDetails, MS, MSC, S, SOC};
mod number;
use number::{Number, NumberParts, NumberValue, Sign};
mod string;
use string::{CborString, PreprocessedStringComponent, String1e};
#[cfg(test)]
mod tests;
use error::*;
const U8MAX: u64 = u8::MAX as _;
const U16MAX: u64 = u16::MAX as _;
const U32MAX: u64 = u32::MAX as _;
#[derive(Debug, Clone, PartialEq)]
pub struct StandaloneItem<'a>(S<'a>, Item<'a>, S<'a>);
#[derive(Debug, Clone, PartialEq)]
pub struct Item<'a>(InnerItem<'a>);
impl<'a> StandaloneItem<'a> {
pub fn parse(s: &'a str) -> Result<Self, ParseError> {
cbordiagnostic::one_item(s).map_err(ParseError)
}
pub fn serialize(&self) -> String {
Unparse::serialize(self)
}
pub fn from_cbor(cbor: &[u8]) -> Result<Self, CborError> {
Ok(Self(S::default(), Item::from_cbor(cbor)?, S::default()))
}
pub fn from_cbor_with_rest(cbor: &[u8]) -> Result<(Self, &[u8]), CborError> {
let (item, rest) = Item::from_cbor_with_rest(cbor)?;
Ok((Self(S::default(), item, S::default()), rest))
}
pub fn to_cbor(&self) -> Result<Vec<u8>, InconsistentEdn> {
Ok(Unparse::to_cbor(self)?.collect())
}
}
impl<'a> StandaloneItem<'a> {
pub fn into_item(self) -> Item<'a> {
self.1
}
pub fn item(&self) -> &Item<'a> {
&self.1
}
pub fn item_mut(&mut self) -> &mut Item<'a> {
&mut self.1
}
fn inner(&self) -> &InnerItem<'a> {
self.1.inner()
}
pub fn cloned<'any>(&self) -> StandaloneItem<'any> {
StandaloneItem(self.0.cloned(), self.1.cloned(), self.2.cloned())
}
}
impl Item<'_> {
pub fn serialize(&self) -> String {
Unparse::serialize(self)
}
pub fn from_cbor(cbor: &[u8]) -> Result<Self, CborError> {
match Self::from_cbor_with_rest(cbor) {
Ok((s, &[])) => Ok(s),
Ok(_) => Err(CborError("Data after item")),
Err(e) => Err(e),
}
}
pub fn from_cbor_with_rest(cbor: &[u8]) -> Result<(Self, &[u8]), CborError> {
let (major, argument, spec, mut tail) = process_cbor_major_argument(cbor)?;
let mut s = match (major, argument, spec) {
(Major::Unsigned, Some(argument), spec) => Self::new_integer_decimal_with_spec(
argument,
spec.or_none_if_default_for_arg(argument),
),
(Major::Negative, Some(argument), spec) => Self::new_integer_decimal_with_spec(
-1i128 - i128::from(argument),
spec.or_none_if_default_for_arg(argument),
),
(Major::FloatSimple, Some(n @ 0..=19), Spec::S_i) => {
Simple::Numeric(Box::new(Self::new_integer_decimal(n).into())).into()
}
(Major::FloatSimple, Some(20), Spec::S_i) => Simple::False.into(),
(Major::FloatSimple, Some(21), Spec::S_i) => Simple::True.into(),
(Major::FloatSimple, Some(22), Spec::S_i) => Simple::Null.into(),
(Major::FloatSimple, Some(23), Spec::S_i) => Simple::Undefined.into(),
(Major::FloatSimple, Some(n @ 32..=255), Spec::S_0) => {
Simple::Numeric(Box::new(Self::new_integer_decimal(n).into())).into()
}
(Major::FloatSimple, _, Spec::S_i | Spec::S_0) => {
return Err(CborError("Invalid simple value"))
}
(Major::FloatSimple, Some(0x7c00), Spec::S_1) => {
Number(Cow::from("Infinity")).with_spec(Some(Spec::S_1))
}
(Major::FloatSimple, Some(0xfc00), Spec::S_1) => {
Number(Cow::from("-Infinity")).with_spec(Some(Spec::S_1))
}
(Major::FloatSimple, Some(0x7e00), Spec::S_1) => {
Number(Cow::from("NaN")).with_spec(Some(Spec::S_1))
}
(Major::FloatSimple, Some(n), Spec::S_1) => {
let f =
float::f16_bits_to_f64(n.try_into().expect("Range limited by construction"));
Number::new_float(f).with_spec(Some(Spec::S_1))
}
(Major::FloatSimple, Some(n), Spec::S_2) => {
let n: u32 = n.try_into().expect("Range limited by construction");
let f = f64::from(f32::from_bits(n));
Number::new_float(f).with_spec(Some(Spec::S_2))
}
(Major::FloatSimple, Some(n), Spec::S_3) => {
let f = f64::from_bits(n);
Number::new_float(f).with_spec(Some(Spec::S_3))
}
(Major::FloatSimple, None, _ )
| (Major::FloatSimple, _ , Spec::S_) => {
return Err(CborError(
"Break code only expected at end of indefinte length items",
))
}
(Major::Tagged, Some(n), s) => {
let (item, new_tail) = StandaloneItem::from_cbor_with_rest(tail)?;
tail = new_tail;
item.tagged_with_spec(n, s.or_none_if_default_for_arg(n))
}
(Major::Unsigned | Major::Negative | Major::Tagged, None, _) => {
return Err(CborError(
"Integer/Tag with indefinite length encoding is not well-formed",
))
}
(Major::ByteString, Some(n), spec) => {
let data = n
.try_into()
.ok()
.and_then(|n| tail.get(..n))
.ok_or(CborError("Announced bytes unavailable"))?;
tail = &tail[data.len()..];
Self::new_bytes_hex_with_spec(data, spec.or_none_if_default_for_arg(n))
}
(Major::TextString, Some(n), spec) => {
let data = n
.try_into()
.ok()
.and_then(|n| tail.get(..n))
.ok_or(CborError("Announced bytes unavailable"))?;
let data = core::str::from_utf8(data)
.map_err(|_| CborError("Text string must be valid UTF-8"))?;
tail = &tail[data.len()..];
Self::new_text_with_spec(data, spec.or_none_if_default_for_arg(n))
}
(
Major::ByteString | Major::TextString,
None,
_,
) => {
let mut items = vec![];
while tail.first() != Some(&0xff) {
let (inner_major, argument, spec, new_tail) =
process_cbor_major_argument(tail)?;
let Some(argument) = argument.and_then(|a| usize::try_from(a).ok()) else {
return Err(CborError(
"Indefinite length strings can only contain definite lengths and must fit in data",
));
};
if inner_major != major {
return Err(CborError(
"Indefinite length strings can only contain matching items",
));
}
if new_tail.len() < argument {
return Err(CborError(
"Announced bytes unavailable inside indefinite length byte string",
));
}
let (item_data, new_tail) = new_tail.split_at(argument);
tail = new_tail;
items.push(match major {
Major::ByteString => {
CborString::new_bytes_hex_with_spec(item_data, Some(spec))
}
Major::TextString => CborString::new_text_with_spec(
core::str::from_utf8(item_data)
.map_err(|_| CborError("Text string must be valid UTF-8"))?,
Some(spec),
),
_ => unreachable!(),
});
}
if tail.is_empty() {
return Err(CborError(
"Indefinite length byte string terminated after item",
));
}
tail = &tail[1..];
let mut items = items.drain(..);
if let Some(first_item) = items.next() {
InnerItem::StreamString(
Default::default(),
NonemptyMscVec::new(first_item, items),
)
.into()
} else {
todo!()
}
}
(Major::Array, mut length, spec) => {
let mut items = vec![];
while length != Some(0) && tail.first() != Some(&0xff) {
let (item, new_tail) = Self::from_cbor_with_rest(tail)?;
items.push(item);
tail = new_tail;
if let Some(ref mut n) = &mut length {
*n -= 1;
}
}
if length.is_none() {
if tail.is_empty() {
return Err(CborError(
"Indefinite length byte string terminated after item",
));
}
tail = &tail[1..];
}
let spec = match length {
Some(l) => spec.or_none_if_default_for_arg(l),
None => Some(spec), };
InnerItem::Array(SpecMscVec::new(spec, items.into_iter())).into()
}
(Major::Map, mut length, spec) => {
let mut items = vec![];
while length != Some(0) && tail.first() != Some(&0xff) {
let (key, new_tail) = Self::from_cbor_with_rest(tail)?;
tail = new_tail;
let (value, new_tail) = Self::from_cbor_with_rest(tail)?;
tail = new_tail;
items.push(Kp::new(key, value));
if let Some(ref mut n) = &mut length {
*n -= 1;
}
}
if length.is_none() {
if tail.is_empty() {
return Err(CborError(
"Indefinite length byte string terminated after item",
));
}
tail = &tail[1..];
}
let spec = match length {
Some(l) => spec.or_none_if_default_for_arg(l),
None => Some(spec), };
InnerItem::Map(SpecMscVec::new(spec, items.into_iter())).into()
}
};
s.set_delimiters(DelimiterPolicy::SingleLineRegularSpacing);
Ok((s, tail))
}
fn visit(&mut self, visitor: &mut impl Visitor) -> ProcessResult {
let mut result = visitor.process(self);
if result.take_recurse() {
self.0.visit(visitor);
}
result
}
pub fn cloned<'any>(&self) -> Item<'any> {
Item(self.0.cloned())
}
}
impl<'a> Item<'a> {
fn inner(&self) -> &InnerItem<'a> {
&self.0
}
fn inner_mut(&mut self) -> &mut InnerItem<'a> {
&mut self.0
}
}
impl<'a> StandaloneItem<'a> {
fn tagged_with_spec(self, tag: u64, spec: Option<Spec>) -> Item<'a> {
InnerItem::Tagged(tag, spec, Box::new(self)).into()
}
pub fn tagged(self, tag: u64) -> Item<'a> {
InnerItem::Tagged(tag, None, Box::new(self)).into()
}
}
impl<'a> Item<'a> {
fn new_integer_decimal_with_spec(value: impl Into<i128>, spec: Option<Spec>) -> Self {
Number(format!("{}", value.into()).into()).with_spec(spec)
}
pub fn new_integer_decimal(value: impl Into<i128>) -> Self {
Self::new_integer_decimal_with_spec(value, None)
}
pub fn new_float_decimal(value: f64) -> Self {
Number::new_float(value).with_spec(None)
}
pub fn new_integer_hex(value: impl Into<u64>) -> Self {
InnerItem::Number(Number(format!("0x{:x}", value.into()).into()), None).into()
}
fn new_bytes_hex_with_spec(value: &[u8], spec: Option<Spec>) -> Self {
InnerItem::String(CborString::new_bytes_hex_with_spec(value, spec)).into()
}
pub fn new_bytes_hex(value: &[u8]) -> Self {
Self::new_bytes_hex_with_spec(value, None)
}
fn new_text_with_spec(value: &str, spec: Option<Spec>) -> Self {
InnerItem::String(CborString::new_text_with_spec(value, spec)).into()
}
pub fn new_text(value: &str) -> Self {
Self::new_text_with_spec(value, None)
}
pub fn new_application_literal(identifier: &str, value: &str) -> Result<Self, InconsistentEdn> {
if cbordiagnostic::app_prefix(identifier).is_err() {
return Err(InconsistentEdn(
"Identifier is not a valid application string identifier",
));
};
Ok(InnerItem::String(CborString::new_application_literal(identifier, value, None)).into())
}
pub fn new_array(items: impl Iterator<Item = Item<'a>>) -> Self {
InnerItem::Array(SpecMscVec::new(None, items)).into()
}
pub fn new_map(items: impl Iterator<Item = (Item<'a>, Item<'a>)>) -> Self {
InnerItem::Map(SpecMscVec::new(
None,
items.map(|(key, value)| Kp::new(key, value)),
))
.into()
}
pub fn tagged(self, tag: u64) -> Item<'a> {
StandaloneItem::from(self).tagged(tag)
}
}
impl StandaloneItem<'_> {
pub fn with_comment(self, comment: &str) -> Self {
let wrapped_comment = if comment.contains('/') {
format!("# {}\n", comment.replace('\n', "\n# "))
} else {
format!("/ {} /", comment)
};
Self(S(wrapped_comment.into()), self.1, self.2)
}
pub fn set_comment(&mut self, comment: &str) {
let wrapped_comment = if comment.contains('/') {
format!("# {}\n", comment.replace('\n', "\n# "))
} else {
format!("/ {} /", comment)
};
self.0 = S(wrapped_comment.into());
}
pub fn set_delimiters(&mut self, policy: DelimiterPolicy) {
self.0.set_delimiters(policy, false);
self.1.set_delimiters(policy);
self.2.set_delimiters(policy, true);
}
fn visit(&mut self, visitor: &mut impl Visitor) {
self.1
.visit(visitor)
.use_space_before(&mut self.0)
.use_space_after(&mut self.2)
.done();
}
pub fn visit_application_literals<F, RF>(&mut self, mut f: RF)
where
F: for<'b> FnMut(String, String, &mut Item<'b>) -> Result<(), String> + ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
self.visit(&mut ApplicationLiteralsVisitor {
user_fn: f.deref_mut(),
});
}
pub fn visit_tag<F, RF>(&mut self, mut f: RF)
where
F: for<'b> FnMut(u64, &mut Item<'b>) -> Result<(), String> + ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
self.visit(&mut TagVisitor {
user_fn: f.deref_mut(),
});
}
}
impl<'a> Item<'a> {
pub fn get_application_literal(&self) -> Result<(String, String), TypeMismatch> {
let InnerItem::String(CborString { ref items, .. }) = self.inner() else {
return Err(TypeMismatch::expecting("application-oriented literal"));
};
let [chunk] = items.as_slice() else {
return Err(TypeMismatch::expecting(
"single application-oriented literal",
));
};
let PreprocessedStringComponent::AppString(identifier, value) = chunk
.preprocess()
.map_err(|_| TypeMismatch::expecting("application-oriented literal"))?
else {
return Err(TypeMismatch::expecting("application-oriented literal"));
};
Ok((identifier, value))
}
pub fn get_bytes(&self) -> Result<Vec<u8>, TypeMismatch> {
let mut result = vec![];
let mut append_items = |items: &Vec<String1e>| -> Result<(), TypeMismatch> {
for item in items {
if item
.encoded_major_type()
.map_err(|_| TypeMismatch::expecting("encodable item"))?
!= Major::ByteString
{
return Err(TypeMismatch::expecting("byte literal"));
}
result.extend(
item.bytes_value()
.map_err(|_| TypeMismatch::expecting("byte literal or compatible"))?,
);
}
Ok(())
};
match self.inner() {
InnerItem::String(CborString { ref items, .. }) => append_items(items)?,
InnerItem::StreamString(_, ref chunks) => {
for CborString { ref items, .. } in chunks.iter() {
append_items(items)?;
}
}
_ => return Err(TypeMismatch::expecting("byte literal")),
}
Ok(result)
}
pub fn get_string(&self) -> Result<String, TypeMismatch> {
let mut result = vec![];
let mut append_items = |items: &Vec<String1e>| -> Result<(), TypeMismatch> {
for item in items {
result.extend(
item.bytes_value()
.map_err(|_| TypeMismatch::expecting("text literal or compatible"))?,
);
}
Ok(())
};
let check_first = |item: &String1e<'_>| -> Result<(), TypeMismatch> {
if item
.encoded_major_type()
.map_err(|_| TypeMismatch::expecting("encodable item"))?
!= Major::TextString
{
return Err(TypeMismatch::expecting("text literal"));
}
Ok(())
};
match self.inner() {
InnerItem::String(CborString { ref items, .. }) => {
check_first(items.first().expect("Part of the type guarantees"))?;
append_items(items)?;
}
InnerItem::StreamString(_, ref chunks) => {
check_first(
chunks
.first
.items
.first()
.expect("Part of the type guarantees"),
)?;
for CborString { ref items, .. } in chunks.iter() {
append_items(items)?;
}
}
_ => return Err(TypeMismatch::expecting("byte literal")),
}
String::from_utf8(result).map_err(|_| TypeMismatch::expecting("valid UTF-8"))
}
pub fn get_tag(&self) -> Result<u64, TypeMismatch> {
let InnerItem::Tagged(tag, _, _) = self.inner() else {
return Err(TypeMismatch::expecting("tagged item"));
};
Ok(*tag)
}
pub fn get_tagged(&self) -> Result<&StandaloneItem<'a>, TypeMismatch> {
let InnerItem::Tagged(_, _, ref item) = self.inner() else {
return Err(TypeMismatch::expecting("tagged item"));
};
Ok(item)
}
pub fn get_tagged_mut(&mut self) -> Result<&mut StandaloneItem<'a>, TypeMismatch> {
let InnerItem::Tagged(_, _, ref mut item) = self.inner_mut() else {
return Err(TypeMismatch::expecting("tagged item"));
};
Ok(item)
}
pub fn get_integer(&self) -> Result<i128, TypeMismatch> {
let InnerItem::Number(ref number, _) = self.inner() else {
return Err(TypeMismatch::expecting("integer"));
};
match number.value() {
NumberValue::Float(_) => Err(TypeMismatch::expecting("integer")),
NumberValue::Positive(n) => Ok(n.into()),
NumberValue::Negative(n) => Ok(-1 - i128::from(n)),
NumberValue::Big(n) => n
.try_into()
.map_err(|_| TypeMismatch::expecting("integer in i128 range")),
}
}
pub fn get_float(&self) -> Result<f64, TypeMismatch> {
let InnerItem::Number(ref number, _) = self.inner() else {
return Err(TypeMismatch::expecting("float"));
};
match number.value() {
NumberValue::Float(f) => Ok(f),
NumberValue::Positive(_) => Err(TypeMismatch::expecting("float (not integer)")),
NumberValue::Negative(_) => Err(TypeMismatch::expecting("float (not integer)")),
NumberValue::Big(_) => Err(TypeMismatch::expecting("float (not integer)")),
}
}
pub fn get_array_items(&self) -> Result<impl Iterator<Item = &Item<'a>>, TypeMismatch> {
let InnerItem::Array(smv) = self.inner() else {
return Err(TypeMismatch::expecting("array"));
};
Ok(smv.iter())
}
pub fn get_array_items_mut(
&mut self,
) -> Result<impl Iterator<Item = &mut Item<'a>>, TypeMismatch> {
let InnerItem::Array(smv) = self.inner_mut() else {
return Err(TypeMismatch::expecting("array"));
};
Ok(smv.iter_mut())
}
pub fn get_map_items(
&self,
) -> Result<impl Iterator<Item = (&Item<'a>, &Item<'a>)>, TypeMismatch> {
let InnerItem::Map(smv) = self.inner() else {
return Err(TypeMismatch::expecting("map"));
};
Ok(smv.iter().map(|kp| (&kp.key, &kp.value)))
}
pub fn get_map_items_mut(
&mut self,
) -> Result<impl Iterator<Item = (&mut Item<'a>, &mut Item<'a>)>, TypeMismatch> {
let InnerItem::Map(smv) = self.inner_mut() else {
return Err(TypeMismatch::expecting("map"));
};
Ok(smv.iter_mut().map(|kp| (&mut kp.key, &mut kp.value)))
}
pub fn discard_encoding_indicators(&mut self) {
self.inner_mut().discard_encoding_indicators();
}
pub fn set_delimiters(&mut self, policy: DelimiterPolicy) {
self.0.set_delimiters(policy);
}
pub fn with_comment(self, comment: &str) -> StandaloneItem<'a> {
let wrapped_comment = if comment.contains('/') {
format!("# {}\n", comment.replace('\n', "\n# "))
} else {
format!("/ {} /", comment)
};
StandaloneItem(S(wrapped_comment.into()), self, S::default())
}
pub fn visit_map_elements<F, RF>(&mut self, mut f: RF) -> Result<(), TypeMismatch>
where
F: for<'b> FnMut(
&mut Item<'b>,
) -> Result<(Option<String>, Option<MapValueHandler>), String>
+ ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
if !matches!(self.0, InnerItem::Map(_)) {
return Err(TypeMismatch::expecting("map"));
}
let f = f.deref_mut();
self.visit(&mut MapElementVisitor::new(f)).done();
Ok(())
}
pub fn visit_array_elements<F, RF>(&mut self, mut f: RF) -> Result<(), TypeMismatch>
where
F: for<'b> FnMut(&mut Item<'b>) -> Result<Option<String>, String> + ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
if !matches!(self.0, InnerItem::Array(_)) {
return Err(TypeMismatch::expecting("array"));
}
let f = f.deref_mut();
self.visit(&mut ArrayElementVisitor::new(f)).done();
Ok(())
}
}
impl Unparse for StandaloneItem<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.serialize_write(formatter)?;
self.1.serialize_write(formatter)?;
self.2.serialize_write(formatter)?;
Ok(())
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
self.1.to_cbor()
}
}
impl Unparse for Item<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.serialize_write(formatter)
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
self.0.to_cbor()
}
}
impl<'a> From<InnerItem<'a>> for StandaloneItem<'a> {
fn from(inner: InnerItem<'a>) -> Self {
Item::from(inner).into()
}
}
impl<'a> From<Item<'a>> for StandaloneItem<'a> {
fn from(inner: Item<'a>) -> Self {
Self(S::default(), inner, S::default())
}
}
impl<'a> From<InnerItem<'a>> for Item<'a> {
fn from(inner: InnerItem<'a>) -> Self {
Item(inner)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Sequence<'a> {
s0: S<'a>,
items: Option<NonemptyMscVec<'a, Item<'a>>>,
}
impl<'a> Sequence<'a> {
pub fn parse(s: &'a str) -> Result<Self, ParseError> {
cbordiagnostic::seq(s).map_err(ParseError)
}
pub fn serialize(&self) -> String {
Unparse::serialize(self)
}
pub fn from_cbor(cbor: &[u8]) -> Result<Self, CborError> {
let mut tail = cbor;
let mut items = vec![];
while !tail.is_empty() {
let (item, new_tail) = Item::from_cbor_with_rest(tail)?;
items.push(item);
tail = new_tail;
}
let mut s = Self::new(items.into_iter());
s.set_delimiters(DelimiterPolicy::SingleLineRegularSpacing);
Ok(s)
}
pub fn to_cbor(&self) -> Result<Vec<u8>, InconsistentEdn> {
Ok(Unparse::to_cbor(self)?.collect())
}
pub fn new(mut items: impl Iterator<Item = Item<'a>>) -> Self {
Sequence {
s0: Default::default(),
items: items.next().map(|first| NonemptyMscVec::new(first, items)),
}
}
pub fn visit_application_literals<F, RF>(&mut self, mut f: RF)
where
F: for<'b> FnMut(String, String, &mut Item<'b>) -> Result<(), String> + ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
self.visit(&mut ApplicationLiteralsVisitor {
user_fn: f.deref_mut(),
});
}
pub fn visit_tag<F, RF>(&mut self, mut f: RF)
where
F: for<'b> FnMut(u64, &mut Item<'b>) -> Result<(), String> + ?Sized,
RF: std::ops::DerefMut<Target = F>,
{
self.visit(&mut TagVisitor {
user_fn: f.deref_mut(),
});
}
pub fn items(&self) -> impl Iterator<Item = &Item<'a>> {
self.items.as_ref().map(|i| i.iter()).into_iter().flatten()
}
pub fn items_mut(&mut self) -> impl Iterator<Item = &mut Item<'a>> {
self.items
.as_mut()
.map(|i| i.iter_mut())
.into_iter()
.flatten()
}
#[deprecated(note = "renamed to items_mut()")]
pub fn get_items_mut(&mut self) -> impl Iterator<Item = &mut Item<'a>> {
self.items_mut()
}
pub fn discard_encoding_indicators(&mut self) {
for i in self.items_mut() {
i.discard_encoding_indicators()
}
}
pub fn set_delimiters(&mut self, policy: DelimiterPolicy) {
self.s0.set_delimiters(policy, false);
if let Some(items) = self.items.as_mut() {
items.first.set_delimiters(policy);
for (msc, item) in items.tail.iter_mut() {
msc.set_delimiters(policy, true);
item.set_delimiters(policy);
}
match policy {
DelimiterPolicy::IndentedRegularSpacing {
trailing_newline: TrailingNewlinePolicy::Always,
..
} => items.soc.set_delimiters(policy, true),
DelimiterPolicy::IndentedRegularSpacing {
trailing_newline: TrailingNewlinePolicy::Never,
..
} => items.soc.set_delimiters(policy, false),
_ => items.soc.set_delimiters(policy, !items.tail.is_empty()),
}
}
}
fn visit(&mut self, visitor: &mut impl Visitor) {
if let Some(nmv) = self.items.as_mut() {
nmv.visit(visitor).use_space_after(&mut self.s0).done();
}
}
pub fn cloned<'any>(&self) -> Sequence<'any> {
Sequence {
s0: self.s0.cloned(),
items: self.items.as_ref().map(|i| i.cloned()),
}
}
}
impl Unparse for Sequence<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
self.s0.serialize_write(formatter)?;
if let Some(items) = self.items.as_ref() {
items.serialize_write(formatter)?;
}
Ok(())
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
let chain = self.items.as_ref().map(|items| items.to_cbor());
let chain = chain.transpose();
chain.map(|optit| optit.into_iter().flatten())
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum DelimiterPolicy {
DiscardAll,
DiscardAllButComments,
SingleLineRegularSpacing,
IndentedRegularSpacing {
base_indent: usize,
indent_level: usize,
max_width: usize,
trailing_newline: TrailingNewlinePolicy,
},
SingleSpace,
}
impl DelimiterPolicy {
pub fn indented() -> Self {
Self::IndentedRegularSpacing {
base_indent: 0,
indent_level: 4,
max_width: 80,
trailing_newline: TrailingNewlinePolicy::IfMultiline,
}
}
pub fn indented_with_final_newline() -> Self {
Self::IndentedRegularSpacing {
base_indent: 0,
indent_level: 4,
max_width: 80,
trailing_newline: TrailingNewlinePolicy::Always,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TrailingNewlinePolicy {
Never,
IfMultiline,
Always,
}
trait Unparse: Sized {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result;
fn serialize(&self) -> String {
struct Unparsed<'a, T: Unparse>(&'a T);
impl<T: Unparse> core::fmt::Display for Unparsed<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.serialize_write(f)
}
}
format!("{}", Unparsed(self))
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn>;
}
#[derive(Debug, Clone, PartialEq)]
struct NonemptyMscVec<'a, T: Unparse> {
first: Box<T>,
tail: Vec<(MSC<'a>, T)>,
soc: SOC<'a>,
}
impl<'a, T: Unparse> NonemptyMscVec<'a, T> {
fn new(first: T, tail: impl Iterator<Item = T>) -> Self {
Self {
first: Box::new(first),
tail: tail.map(|i| (Default::default(), i)).collect(),
soc: Default::default(),
}
}
fn new_parsing(first: T, tail: Vec<(MSC<'a>, T)>, soc: SOC<'a>) -> Self {
Self {
first: Box::new(first),
tail,
soc,
}
}
fn len(&self) -> usize {
1 + self.tail.len()
}
fn iter(&self) -> impl Iterator<Item = &T> {
core::iter::once(&*self.first).chain(self.tail.iter().map(|(_msc, t)| t))
}
}
impl<'a> NonemptyMscVec<'a, Item<'a>> {
fn visit(&mut self, visitor: &mut impl Visitor) -> ProcessResult {
let mut own_result = self.first.visit(visitor);
let mut last_result: Option<ProcessResult> = None;
for (msc, item) in self.tail.iter_mut() {
if let Some(result) = last_result.take() {
result.use_space_after(msc).done();
} else {
own_result = own_result.use_space_after(msc);
}
let item_result = item.visit(visitor);
let replaced = last_result.replace(item_result.use_space_before(msc));
assert!(replaced.is_none());
}
if let Some(result) = last_result.take() {
result.use_space_after(&mut self.soc).done();
} else {
own_result = own_result.use_space_after(&mut self.soc);
}
own_result
}
fn cloned<'any>(&self) -> NonemptyMscVec<'any, Item<'any>> {
NonemptyMscVec {
first: Box::new(self.first.cloned()),
tail: self
.tail
.iter()
.map(|(msc, i)| (msc.cloned(), i.cloned()))
.collect(),
soc: self.soc.cloned(),
}
}
}
impl<'a> NonemptyMscVec<'a, Kp<'a>> {
fn visit(&mut self, visitor: &mut impl Visitor) -> ProcessResult {
let mut own_result = self.first.visit(visitor);
let mut last_result: Option<ProcessResult> = None;
for (msc, item) in self.tail.iter_mut() {
if let Some(result) = last_result.take() {
result.use_space_after(msc).done();
} else {
own_result = own_result.use_space_after(msc);
}
let item_result = item.visit(visitor);
let replaced = last_result.replace(item_result.use_space_before(msc));
assert!(replaced.is_none());
}
if let Some(result) = last_result.take() {
result.use_space_after(&mut self.soc).done();
} else {
own_result = own_result.use_space_after(&mut self.soc);
}
own_result
}
fn cloned<'any>(&self) -> NonemptyMscVec<'any, Kp<'any>> {
NonemptyMscVec {
first: Box::new(self.first.cloned()),
tail: self
.tail
.iter()
.map(|(msc, i)| (msc.cloned(), i.cloned()))
.collect(),
soc: self.soc.cloned(),
}
}
}
impl<'a> NonemptyMscVec<'a, CborString<'a>> {
fn cloned<'any>(&self) -> NonemptyMscVec<'any, CborString<'any>> {
NonemptyMscVec {
first: Box::new(self.first.cloned()),
tail: self
.tail
.iter()
.map(|(msc, i)| (msc.cloned(), i.cloned()))
.collect(),
soc: self.soc.cloned(),
}
}
}
macro_rules! nmv_concrete_impl {
($t:ident) => {
impl<'a> NonemptyMscVec<'a, $t<'a>> {
fn iter_mut(&mut self) -> impl Iterator<Item = &mut $t<'a>> {
let first: &mut $t<'a> = &mut self.first;
let tail = &mut self.tail;
core::iter::once(first).chain(tail.iter_mut().map(|(_msc, i)| i))
}
}
};
}
nmv_concrete_impl!(Item);
nmv_concrete_impl!(CborString);
impl<T: Unparse> Unparse for NonemptyMscVec<'_, T> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
self.first.serialize_write(formatter)?;
for (msc, item) in self.tail.iter() {
msc.serialize_write(formatter)?;
item.serialize_write(formatter)?;
}
self.soc.serialize_write(formatter)?;
Ok(())
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
let collected: Result<Vec<_>, _> = self.iter().map(Unparse::to_cbor).collect();
Ok(collected?.into_iter().flatten())
}
}
#[derive(Debug, Clone, PartialEq)]
enum SpecMscVec<'a, T: Unparse> {
Present {
spec: Option<(Spec, MS<'a>)>,
s: S<'a>,
items: NonemptyMscVec<'a, T>,
},
Absent {
spec: Option<Spec>,
s: S<'a>,
},
}
impl<T: Unparse> SpecMscVec<'_, T> {
fn new(spec: Option<Spec>, mut items: impl Iterator<Item = T>) -> Self {
if let Some(first) = items.next() {
SpecMscVec::Present {
spec: spec.map(|spec| (spec, Default::default())),
s: Default::default(),
items: NonemptyMscVec::new(first, items),
}
} else {
SpecMscVec::Absent {
spec,
s: Default::default(),
}
}
}
fn len(&self) -> usize {
match self {
SpecMscVec::Present { items, .. } => items.len(),
SpecMscVec::Absent { .. } => 0,
}
}
fn spec(&self) -> Option<Spec> {
match self {
SpecMscVec::Present {
spec: Some((spec, _ms)),
..
} => Some(*spec),
SpecMscVec::Present { spec: None, .. } => None,
SpecMscVec::Absent { spec, .. } => *spec,
}
}
fn iter(&self) -> impl Iterator<Item = &T> {
let (first, tail) = match self {
SpecMscVec::Absent { .. } => (None, None),
SpecMscVec::Present {
items: NonemptyMscVec { first, tail, .. },
..
} => (Some(first.as_ref()), Some(tail)),
};
first
.into_iter()
.chain(tail.into_iter().flatten().map(|(_msc, i)| i))
}
fn discard_own_encoding_indicator(&mut self) {
match self {
SpecMscVec::Absent { spec, .. } => *spec = None,
SpecMscVec::Present { spec, s, .. } => {
if let Some((_spec, ms)) = spec.take() {
if ms != Default::default() {
s.prefix(ms.0);
}
}
}
}
}
}
macro_rules! smv_concrete_impl {
($t:ident) => {
impl<'a> SpecMscVec<'a, $t<'a>> {
fn iter_mut(&mut self) -> impl Iterator<Item = &mut $t<'a>> {
let (first, tail) = match self {
SpecMscVec::Absent { .. } => (None, None),
SpecMscVec::Present {
items: NonemptyMscVec { first, tail, .. },
..
} => (Some(first.as_mut()), Some(tail)),
};
first
.into_iter()
.chain(tail.into_iter().flatten().map(|(_msc, i)| i))
}
fn cloned<'any>(&self) -> SpecMscVec<'any, $t<'any>> {
match self {
SpecMscVec::Present { spec, s, items } => SpecMscVec::Present {
spec: spec.as_ref().map(|(spec, ms)| (*spec, ms.cloned())),
s: s.cloned(),
items: items.cloned(),
},
SpecMscVec::Absent { spec, s } => SpecMscVec::Absent {
spec: spec.map(|s| s.clone()),
s: s.cloned(),
},
}
}
}
};
}
smv_concrete_impl!(Item);
smv_concrete_impl!(Kp);
impl<'a> SpecMscVec<'a, Item<'a>> {
fn visit(&mut self, visitor: &mut impl Visitor) {
match self {
SpecMscVec::Present { spec: _, s, items } => {
items.visit(visitor).use_space_before(s).done();
}
SpecMscVec::Absent { spec: _, s: _ } => (),
}
}
}
impl<'a> SpecMscVec<'a, Kp<'a>> {
fn visit(&mut self, visitor: &mut impl Visitor) {
match self {
SpecMscVec::Present { spec: _, s, items } => {
items.visit(visitor).use_space_before(s).done();
}
SpecMscVec::Absent { spec: _, s: _ } => (),
}
}
}
impl<T: Unparse> Unparse for SpecMscVec<'_, T> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
SpecMscVec::Present { spec, s, items } => {
if let Some((spec, msc)) = spec {
spec.serialize_write(formatter)?;
msc.serialize_write(formatter)?;
}
s.serialize_write(formatter)?;
items.serialize_write(formatter)?;
Ok(())
}
SpecMscVec::Absent { spec, s } => {
if let Some(spec) = spec {
spec.serialize_write(formatter)?;
}
s.serialize_write(formatter)?;
Ok(())
}
}
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
let collected: Result<Vec<_>, _> = self.iter().map(Unparse::to_cbor).collect();
Ok(collected?.into_iter().flatten())
}
}
#[derive(Debug, Clone, PartialEq)]
struct Kp<'a> {
key: Item<'a>,
s0: S<'a>,
s1: S<'a>,
value: Item<'a>,
}
impl<'a> Kp<'a> {
fn new(key: Item<'a>, value: Item<'a>) -> Self {
Self {
key,
s0: Default::default(),
s1: Default::default(),
value,
}
}
fn visit(&mut self, visitor: &mut impl Visitor) -> ProcessResult {
let key_result = self.key.visit(visitor);
let value_result = self.value.visit(visitor);
key_result
.use_space_after(&mut self.s0)
.chain(value_result.use_space_before(&mut self.s1))
}
fn cloned<'any>(&self) -> Kp<'any> {
Kp {
key: self.key.cloned(),
s0: self.s0.cloned(),
s1: self.s1.cloned(),
value: self.value.cloned(),
}
}
}
impl Unparse for Kp<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
self.key.serialize_write(formatter)?;
self.s0.serialize_write(formatter)?;
formatter.write_str(":")?;
self.s1.serialize_write(formatter)?;
self.value.serialize_write(formatter)?;
Ok(())
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
Ok([self.key.to_cbor()?, self.value.to_cbor()?]
.into_iter()
.flatten())
}
}
#[derive(Debug, Clone, PartialEq)]
enum Simple<'a> {
False,
True,
Null,
Undefined,
Numeric(Box<StandaloneItem<'a>>),
}
impl Simple<'_> {
pub(crate) fn cloned<'any>(&self) -> Simple<'any> {
match self {
Simple::False => Simple::False,
Simple::True => Simple::True,
Simple::Null => Simple::Null,
Simple::Undefined => Simple::Undefined,
Simple::Numeric(standalone_item) => Simple::Numeric(Box::new(standalone_item.cloned())),
}
}
}
impl Unparse for Simple<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Simple::False => formatter.write_str("false")?,
Simple::True => formatter.write_str("true")?,
Simple::Null => formatter.write_str("null")?,
Simple::Undefined => formatter.write_str("undefined")?,
Simple::Numeric(i) => {
formatter.write_str("simple(")?;
i.serialize_write(formatter)?;
formatter.write_str(")")?;
}
}
Ok(())
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
let mut result = Vec::new();
match self {
Simple::False => result.push(0xf4),
Simple::True => result.push(0xf5),
Simple::Null => result.push(0xf6),
Simple::Undefined => result.push(0xf7),
Simple::Numeric(i) => {
let InnerItem::Number(ref number, spec) = i.inner() else {
return Err(InconsistentEdn(
"Items inside simple() need to be numbers for serialization.",
));
};
let NumberValue::Positive(number) = number.value() else {
return Err(InconsistentEdn(
"Non-positive numbers can not be in a Simple",
));
};
if number > 255 {
return Err(InconsistentEdn("Spec exceeds valid range of 0..=255"));
}
let requested = Spec::encode_argument(spec.as_ref(), Major::FloatSimple, number)?;
let permissible = Spec::encode_argument(None, Major::FloatSimple, number)?;
if requested != permissible {
return Err(InconsistentEdn(
"Encoding indicators on simple value must use the preferred encoding",
));
}
result.extend(permissible);
}
};
Ok(result.into_iter())
}
}
impl<'a> From<Simple<'a>> for Item<'a> {
fn from(input: Simple<'a>) -> Self {
InnerItem::Simple(input).into()
}
}
#[derive(Clone, Debug, PartialEq)]
enum InnerItem<'a> {
Map(SpecMscVec<'a, Kp<'a>>),
Array(SpecMscVec<'a, Item<'a>>),
Tagged(u64, Option<Spec>, Box<StandaloneItem<'a>>),
Number(Number<'a>, Option<Spec>),
Simple(Simple<'a>),
String(CborString<'a>),
StreamString(MS<'a>, NonemptyMscVec<'a, CborString<'a>>),
}
impl InnerItem<'_> {
fn discard_encoding_indicators(&mut self) {
match self {
InnerItem::Map(items) => {
for i in items.iter_mut() {
i.key.discard_encoding_indicators();
i.value.discard_encoding_indicators();
}
items.discard_own_encoding_indicator();
}
InnerItem::Array(items) => {
for i in items.iter_mut() {
i.discard_encoding_indicators();
}
items.discard_own_encoding_indicator();
}
InnerItem::Tagged(_n, spec, item) => {
*spec = None;
item.item_mut().discard_encoding_indicators();
}
InnerItem::Number(_n, spec) => {
*spec = None;
}
InnerItem::Simple(Simple::Numeric(i)) => i.item_mut().discard_encoding_indicators(),
InnerItem::Simple(_) => {}
InnerItem::String(items) => {
items.discard_encoding_indicators();
}
InnerItem::StreamString(_ms, items) => {
for i in items.iter_mut() {
i.discard_encoding_indicators();
}
}
}
}
fn set_delimiters(&mut self, policy: DelimiterPolicy) {
use DelimiterPolicy::*;
let nested_policy = if let IndentedRegularSpacing {
base_indent,
indent_level,
max_width,
trailing_newline,
} = policy
{
self.set_delimiters(SingleLineRegularSpacing);
if self.serialize().len() + base_indent < max_width {
return;
}
IndentedRegularSpacing {
base_indent: base_indent + indent_level,
indent_level,
max_width,
trailing_newline,
}
} else {
policy
};
match self {
InnerItem::Map(items) => match items {
SpecMscVec::Absent { s, .. } => s.set_delimiters(nested_policy, false),
SpecMscVec::Present { s, items, .. } => {
s.set_delimiters(nested_policy, true);
let set_on_item = |kp: &mut Kp| {
kp.key.set_delimiters(nested_policy);
kp.value.set_delimiters(nested_policy);
kp.s0.set_delimiters(nested_policy, false);
if matches!(policy, SingleLineRegularSpacing) {
kp.s1.0 = " ".into();
} else {
kp.s1.set_delimiters(nested_policy, false);
}
};
set_on_item(&mut items.first);
for (msc, item) in items.tail.iter_mut() {
set_on_item(item);
msc.set_delimiters(nested_policy, true);
}
items.soc.set_delimiters(policy, true);
}
},
InnerItem::Array(items) => match items {
SpecMscVec::Absent { s, .. } => s.set_delimiters(nested_policy, false),
SpecMscVec::Present { s, items, .. } => {
s.set_delimiters(nested_policy, true);
items.first.set_delimiters(nested_policy);
for (msc, item) in items.tail.iter_mut() {
item.set_delimiters(nested_policy);
msc.set_delimiters(nested_policy, true);
}
items.soc.set_delimiters(policy, true);
}
},
InnerItem::Tagged(_n, _spec, item) => {
item.set_delimiters(nested_policy);
}
InnerItem::Number(_n, _spec) => {}
InnerItem::Simple(Simple::Numeric(item)) => {
item.0.set_delimiters(nested_policy, false);
item.1.set_delimiters(nested_policy);
item.2.set_delimiters(nested_policy, false);
}
InnerItem::Simple(_) => {}
InnerItem::String(CborString { items, separators }) => {
for i in items {
i.set_delimiters(nested_policy);
}
for (sep_pre, sep_post) in separators {
match nested_policy {
SingleLineRegularSpacing => {
sep_pre.set_delimiters(SingleSpace, true);
sep_post.set_delimiters(SingleSpace, false);
}
_ => {
sep_pre.set_delimiters(nested_policy, true);
sep_post.set_delimiters(nested_policy, false);
}
}
}
}
InnerItem::StreamString(ms, NonemptyMscVec { first, tail, soc }) => {
ms.set_delimiters(nested_policy, true);
first.set_delimiters(nested_policy);
for (ms, item) in tail {
ms.set_delimiters(nested_policy, true);
item.set_delimiters(nested_policy);
}
soc.set_delimiters(policy, true);
}
}
}
fn visit(&mut self, visitor: &mut impl Visitor) {
match self {
InnerItem::Map(spec_msc_vec) => {
spec_msc_vec.visit(visitor);
}
InnerItem::Array(spec_msc_vec) => {
spec_msc_vec.visit(visitor);
}
InnerItem::Tagged(_number, _spec, standalone_item) => {
standalone_item.visit(visitor);
}
InnerItem::Number(_number, _spec) => (),
InnerItem::Simple(_simple) => (),
InnerItem::String(_cbor_string) => (),
InnerItem::StreamString(_ms, _nonempty_msc_vec) => (),
}
}
fn cloned<'any>(&self) -> InnerItem<'any> {
match self {
InnerItem::Map(spec_msc_vec) => InnerItem::Map(spec_msc_vec.cloned()),
InnerItem::Array(spec_msc_vec) => InnerItem::Array(spec_msc_vec.cloned()),
InnerItem::Tagged(tag, spec, standalone_item) => {
InnerItem::Tagged(*tag, *spec, Box::new(standalone_item.cloned()))
}
InnerItem::Number(number, spec) => InnerItem::Number(number.cloned(), *spec),
InnerItem::Simple(simple) => InnerItem::Simple(simple.cloned()),
InnerItem::String(cbor_string) => InnerItem::String(cbor_string.cloned()),
InnerItem::StreamString(ms, nonempty_msc_vec) => {
InnerItem::StreamString(ms.cloned(), nonempty_msc_vec.cloned())
}
}
}
}
impl Unparse for InnerItem<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
InnerItem::Map(items) => {
write!(formatter, "{{")?;
items.serialize_write(formatter)?;
write!(formatter, "}}")?;
Ok(())
}
InnerItem::Array(items) => {
write!(formatter, "[")?;
items.serialize_write(formatter)?;
write!(formatter, "]")?;
Ok(())
}
InnerItem::Tagged(n, spec, item) => {
write!(formatter, "{}", n)?;
if let Some(spec) = spec {
spec.serialize_write(formatter)?;
}
formatter.write_str("(")?;
item.serialize_write(formatter)?;
formatter.write_str(")")?;
Ok(())
}
InnerItem::Number(n, spec) => {
formatter.write_str(&n.0)?;
if let Some(spec) = spec {
spec.serialize_write(formatter)?;
}
Ok(())
}
InnerItem::Simple(s) => s.serialize_write(formatter),
InnerItem::String(s) => s.serialize_write(formatter),
InnerItem::StreamString(ms, nmv) => {
formatter.write_str("(_")?;
ms.serialize_write(formatter)?;
nmv.serialize_write(formatter)?;
formatter.write_str(")")?;
Ok(())
}
}
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
let mut result = vec![];
match self {
InnerItem::Map(smv) => {
let len = smv.len();
let spec = smv.spec();
let (head, tail) = Spec::encode_item_count(spec.as_ref(), Major::Map, len)?;
result.extend(head);
for i in smv.iter() {
result.extend(i.to_cbor()?);
}
result.extend(tail);
}
InnerItem::Array(smv) => {
let len = smv.len();
let spec = smv.spec();
let (head, tail) = Spec::encode_item_count(spec.as_ref(), Major::Array, len)?;
result.extend(head);
for i in smv.iter() {
result.extend(i.to_cbor()?);
}
result.extend(tail);
}
InnerItem::Tagged(n, spec, item) => {
result.extend(Spec::encode_argument(spec.as_ref(), Major::Tagged, *n)?);
result.extend(item.to_cbor()?);
}
InnerItem::Number(n, spec) => match n.value() {
NumberValue::Positive(n) => {
result.extend(Spec::encode_argument(spec.as_ref(), Major::Unsigned, n)?)
}
NumberValue::Negative(n) => {
result.extend(Spec::encode_argument(spec.as_ref(), Major::Negative, n)?)
}
NumberValue::Float(n) => result.extend(float::encode(n, *spec)?),
NumberValue::Big(n) => match spec {
None => {
let (tag, positive) = if n >= num_bigint::BigInt::ZERO {
(2, n)
} else {
(3, -n)
};
use num_traits::ops::bytes::ToBytes;
result.extend(Spec::encode_argument(None, Major::Tagged, tag)?);
let bytes = positive.to_be_bytes();
result.extend(Spec::encode_argument(
None,
Major::ByteString,
bytes
.len()
.try_into()
.expect("Even on 128-bit systems, EDN does not exceed 64bit sizes"),
)?);
result.extend(bytes);
}
_ => {
return Err(InconsistentEdn(
"Encoding indicators not specified for bignums",
))
}
},
},
InnerItem::Simple(s) => result.extend(s.to_cbor()?),
InnerItem::String(s) => result.extend(s.to_cbor()?),
InnerItem::StreamString(_ms, NonemptyMscVec { first, tail, .. }) => {
let major = first.encoded_major_type()?;
if !matches!(major, Major::TextString | Major::ByteString) {
return Err(InconsistentEdn(
"Item in indefinite length string that is neither bytes nor string",
));
}
result.push(((major as u8) << 5) | 31);
result.extend(first.to_cbor()?);
for item in tail.iter() {
if item.1.encoded_major_type()? != major {
return Err(InconsistentEdn("Item in indefinite length string has different encoding than head element"));
}
result.extend(item.1.to_cbor()?);
}
result.push(0xff);
}
}
Ok(result.into_iter())
}
}
#[derive(PartialEq, Debug, Copy, Clone)]
enum Major {
Unsigned = 0,
Negative = 1,
ByteString = 2,
TextString = 3,
Array = 4,
Map = 5,
Tagged = 6,
FloatSimple = 7,
}
impl Major {
fn from_byte(byte: u8) -> (Self, u8) {
(
match byte >> 5 {
0 => Major::Unsigned,
1 => Major::Negative,
2 => Major::ByteString,
3 => Major::TextString,
4 => Major::Array,
5 => Major::Map,
6 => Major::Tagged,
7 => Major::FloatSimple,
_ => unreachable!(),
},
byte & 0x1f,
)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)] enum Spec {
S_,
S_i,
S_0,
S_1,
S_2,
S_3,
}
impl Spec {
fn encode_item_count(
self_: Option<&Self>,
major: Major,
count: usize,
) -> Result<(Vec<u8>, &[u8]), InconsistentEdn> {
debug_assert!(matches!(major, Major::Map | Major::Array), "Encoding an item count only makes see for maps and arrays; strings work a bit different.");
Ok((
Spec::encode_argument(self_, major, count.try_into().expect("Even on 128bit architectures we can't have more than 64bit long counts of items"))?,
if matches!(self_, Some(Spec::S_)) { [0xff].as_slice() } else { [].as_slice() },
))
}
fn encode_argument(
self_: Option<&Self>,
major: Major,
argument: u64,
) -> Result<Vec<u8>, InconsistentEdn> {
let full_spec = match (self_, argument) {
(None, 0..=23) => Self::S_i,
(None, 0..=U8MAX) => Self::S_0,
(None, 0..=U16MAX) => Self::S_1,
(None, 0..=U32MAX) => Self::S_2,
(None, _) => Self::S_3,
(Some(s), _) => *s,
};
let immediate_value = match full_spec {
Self::S_ => 31,
Self::S_i => {
if argument < 24 {
argument as u8
} else {
return Err(InconsistentEdn(
"Immediate encoding demanded but value exceeds 23",
));
}
}
Self::S_0 => 24,
Self::S_1 => 25,
Self::S_2 => 26,
Self::S_3 => 27,
};
let first = core::iter::once(((major as u8) << 5) | immediate_value);
Ok(match full_spec {
Self::S_ | Self::S_i => first.collect(),
Self::S_0 => first.chain(u8::try_from(argument)?.to_be_bytes()).collect(),
Self::S_1 => first
.chain(u16::try_from(argument)?.to_be_bytes())
.collect(),
Self::S_2 => first
.chain(u32::try_from(argument)?.to_be_bytes())
.collect(),
Self::S_3 => first.chain(argument.to_be_bytes()).collect(),
})
}
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Self::S_ => formatter.write_str("_"),
Self::S_i => formatter.write_str("_i"),
Self::S_0 => formatter.write_str("_0"),
Self::S_1 => formatter.write_str("_1"),
Self::S_2 => formatter.write_str("_2"),
Self::S_3 => formatter.write_str("_3"),
}
}
fn or_none_if_default_for_arg(self, arg: u64) -> Option<Self> {
const U8MAXPLUS: u64 = U8MAX + 1;
const U16MAXPLUS: u64 = U16MAX + 1;
const U32MAXPLUS: u64 = U32MAX + 1;
match (self, arg) {
(Spec::S_i, 0..=23) => None,
(Spec::S_0, 24..=U8MAX) => None,
(Spec::S_1, U8MAXPLUS..=U16MAX) => None,
(Spec::S_2, U16MAXPLUS..=U32MAX) => None,
(Spec::S_3, U32MAXPLUS..=u64::MAX) => None,
(s, _) => Some(s),
}
}
}
impl core::str::FromStr for Spec {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"" => Ok(Self::S_),
"i" => Ok(Self::S_i),
"0" => Ok(Self::S_0),
"1" => Ok(Self::S_1),
"2" => Ok(Self::S_2),
"3" => Ok(Self::S_3),
_ => Err("Unsupported encoding indicator"),
}
}
}
#[allow(clippy::type_complexity)]
fn process_cbor_major_argument(
cbor: &[u8],
) -> Result<(Major, Option<u64>, Spec, &[u8]), CborError> {
let head = cbor
.first()
.ok_or(CborError("Expected item, out of data"))?;
let (major, additional) = Major::from_byte(*head);
let tail = &cbor[1..];
let (argument, spec, skip): (Option<u64>, _, _) = match additional {
0..=23 => (Some(additional.into()), Spec::S_i, 0),
24 => (
Some(
tail.first()
.copied()
.ok_or(CborError("Missing 1 byte"))?
.into(),
),
Spec::S_0,
1,
),
25 => (
Some(
u16::from_be_bytes(
tail.get(..2)
.ok_or(CborError("Missing 2 bytes"))?
.try_into()
.unwrap(),
)
.into(),
),
Spec::S_1,
2,
),
26 => (
Some(
u32::from_be_bytes(
tail.get(..4)
.ok_or(CborError("Missing 4 bytes"))?
.try_into()
.unwrap(),
)
.into(),
),
Spec::S_2,
4,
),
27 => (
Some(u64::from_be_bytes(
tail.get(..8)
.ok_or(CborError("Missing 8 bytes"))?
.try_into()
.unwrap(),
)),
Spec::S_3,
8,
),
31 => (None, Spec::S_, 0),
_ => return Err(CborError("Reserved header byte")),
};
Ok((major, argument, spec, &tail[skip..]))
}
peg::parser! { grammar cbordiagnostic() for str {
pub rule seq() -> Sequence<'input>
= s0:S() items:(first:item() tail:(msc:MSC() inner:item() { (msc, inner) })* soc:SOC() { NonemptyMscVec::new_parsing(first, tail, soc) })? {
Sequence { s0, items }
}
pub rule one_item() -> StandaloneItem<'input>
= s1:S() i:item() s2:S() { StandaloneItem(s1, i, s2) }
rule item() -> Item<'input>
= inner:(map() / array() / tagged() /
number() / simple() /
string:string() { InnerItem::String(string) } / streamstring()) { inner.into() }
rule string1() -> String1e<'input>
= value:$(tstr() / bstr()) spec:spec() {?
Ok(if value.starts_with("<<") {
String1e::EmbeddedChunk(cbordiagnostic::seq(&value[2..value.len() - 2]).map_err(|_| "Parse error in embedded CBOR")?, spec)
} else {
String1e::TextChunk(Cow::Borrowed(value), spec)
})
}
rule string1e() -> String1e<'input>
= string1() / ellipsis()
rule ellipsis() -> String1e<'input>
= dots:$("."*<3,>) { String1e::Ellipsis(dots.len()) }
rule string() -> CborString<'input>
= head:string1e() tail:(separator:S() "+" s1:S() inner:string1e() { (separator, s1, inner) })* {
CborString {
items: core::iter::once(head).chain(tail.iter().map(|(_sep_pre, _sep_post, inner)| inner).cloned()).collect(),
separators: tail.iter().map(|(sep_pre, sep_post, _inner)| (sep_pre.clone(), sep_post.clone())).collect()
}
}
rule number() -> InnerItem<'input>
= num:$((hexfloat() / hexint() / octint() / binint() / decnumber() / nonfin())) spec:spec() {InnerItem::Number(Number(Cow::Borrowed(num)), spec)}
rule sign() -> Sign
= "+" { Sign::Plus } / "-" { Sign::Minus }
pub rule decnumber() -> NumberParts<'input>
= sign:sign()? prepost:(predot:$(DIGIT()+) postdot:("." postdot:$(DIGIT()*) { postdot })? { (predot, postdot) } / "." postdot:$(DIGIT()+) { ("", Some(postdot)) })
exponent:(['e'|'E'] sign:sign()? exponent:$(DIGIT()+) {(sign, exponent)})?
{
let (predot, postdot) = prepost;
NumberParts {
base: 10,
sign,
predot,
postdot,
exponent,
}
}
pub rule hexfloat() -> NumberParts<'input>
= sign:sign()?
"0" ['x'|'X']
prepost:(
predot:$(HEXDIG()+) postdot:("." postdot:$(HEXDIG()*) { postdot })?
{ (Some(predot), postdot) }
/ "." postdot:$(HEXDIG()+)
{ (None, Some(postdot)) }
)
['p'|'P']
expsign:sign()?
exp:$(DIGIT()+)
{
NumberParts {
base: 16,
sign,
predot: prepost.0.unwrap_or(""),
postdot: prepost.1,
exponent: Some((expsign, exp))
}
}
pub rule hexint() -> NumberParts<'input>
= sign:sign()? "0" ['x'|'X'] predot:$(HEXDIG()+) { NumberParts {base: 16, sign, predot, postdot: None, exponent: None} }
pub rule octint() -> NumberParts<'input>
= sign:sign()? "0" ['o'|'O'] predot:$(ODIGIT()+) { NumberParts {base: 8, sign, predot, postdot: None, exponent: None} }
pub rule binint() -> NumberParts<'input>
= sign:sign()? "0" ['b'|'B'] predot:$(BDIGIT()+) { NumberParts {base: 2, sign, predot, postdot: None, exponent: None} }
rule nonfin()
= "Infinity" / "-Infinity" / "NaN"
rule simple() -> InnerItem<'input>
= "false" { InnerItem::Simple(Simple::False) }
/ "true" { InnerItem::Simple(Simple::True) }
/ "null" { InnerItem::Simple(Simple::Null) }
/ "undefined" { InnerItem::Simple(Simple::Undefined) }
/ "simple(" s1:S() i:item() s2:S() ")" {InnerItem::Simple(Simple::Numeric(Box::new(StandaloneItem(s1, i, s2))))}
rule uint() -> u64
= n:$("0" / DIGIT1() DIGIT()*) {? n.parse().or(Err("Exceeding tag space")) }
rule tagged() -> InnerItem<'input>
= tag:uint() tagspec:spec() "(" s0:S() value:item() s1:S() ")" { InnerItem::Tagged(tag, tagspec, Box::new(StandaloneItem(s0, value, s1))) }
pub rule app_prefix() =
quiet!{lcalpha() lcalnum()* / ucalpha() ucalnum()*} / expected!("application prefix")
pub rule app_string() -> (&'input str, String)
= prefix:$(app_prefix()) data:sqstr() { (prefix, data) }
pub rule sqstr() -> String = SQUOTE() sqstr:single_quoted()* SQUOTE() { sqstr.iter().filter_map(|c| *c).collect() }
rule bstr()
= app_string() / sqstr() / embedded()
pub rule tstr() -> String
= DQUOTE() text:double_quoted()* DQUOTE() { text.iter().filter_map(|c| *c).collect() }
rule embedded()
= "<<" seq() ">>"
rule array() -> InnerItem<'input>
= "[" array:(
spec:specms() s:S() first:item() tail:(msc:MSC() inner:item() { (msc, inner) })* soc:SOC()
{ SpecMscVec::Present { spec, s, items: NonemptyMscVec::new_parsing(first, tail, soc) } }
/ spec:spec() s:S()
{ SpecMscVec::Absent { spec, s } }
) "]"
{ InnerItem::Array(array) }
rule map() -> InnerItem<'input>
= "{" map:(
spec:specms() s:S() first:keyp() tail:(msc:MSC() inner:keyp() { (msc, inner) })* soc:SOC()
{ SpecMscVec::Present { spec, s, items: NonemptyMscVec::new_parsing(first, tail, soc) } }
/ spec:spec() s:S()
{ SpecMscVec::Absent { spec, s } }
) "}"
{ InnerItem::Map(map) }
rule keyp() -> Kp<'input>
= key:item() s0:S() ":" s1:S() value:item() { Kp { key, s0, s1, value } }
rule blank() -> ()
= quiet!{"\x09" / "\x0A" / "\x0D" / "\x20"} / expected!("tabs, spaces or newlines")
rule non_slash() -> ()
= blank() / ['\x21'..='\x2e' | '\x30'..='\u{D7FF}' | '\u{E000}'..='\u{10FFFF}'] {}
rule non_lf() -> ()
= ['\x09' | '\x0D' | '\x20'..='\u{D7FF}' | '\u{E000}'..='\u{10FFFF}'] {}
rule comment() -> Comment
= quiet!{"/" body:$(non_slash()*) "/" { Comment::Slashed } / "#" body:$(non_lf()*) "\x0A" { Comment::Hashed }} / expected!("comment")
rule S() -> S<'input>
= data:S_details() { S(Cow::Borrowed(data.data)) }
pub(crate) rule S_details() -> SDetails<'input>
= sliced:with_slice(<blank()* comments:(comment:comment() blank()* { comment })* { comments.last().cloned() }>) { SDetails { data: sliced.1, last_comment_style: sliced.0 } }
rule MS() -> MS<'input>
= data:$( (blank() / comment() ) S()) { MS(Cow::Borrowed(data)) }
rule MSC() -> MSC<'input>
= data:$( ("," S()) / (MS() ("," S())?) ) { MSC(Cow::Borrowed(data)) }
rule SOC() -> SOC<'input>
= data:$( SOC_details() ) { SOC(Cow::Borrowed(data)) }
pub(crate) rule SOC_details() -> (SDetails<'input>, Option<SDetails<'input>>)
= before:S_details() after:("," after:S_details() { after })? { (before, after) }
rule streamstring() -> InnerItem<'input>
= "(_" ms:MS() first:string() tail:(msc:MSC() inner:string() { (msc, inner) })* soc:SOC() ")" {
InnerItem::StreamString(ms, NonemptyMscVec::new_parsing(first, tail, soc))
}
rule spec() -> Option<Spec>
= quiet!{("_" spec:$(wordchar()*) {? spec.parse() })? } / expected!(r#"a valid encoding indicator ("_", "_i", "_0", "_1", "_2" or "_3")"#)
rule specms() -> Option<(Spec, MS<'input>)>
= quiet!{("_" spec:$(wordchar()*) ms:MS() {? spec.parse().map(|spec| (spec, ms)) })? } / expected!(r#"a valid encoding indicator ("_", "_i", "_0", "_1", "_2" or "_3")"#)
rule double_quoted() -> Option<char>
= unescaped() /
SQUOTE() { Some('\'') } /
"\\" DQUOTE() { Some('"') } /
"\\" e:escapable() { Some(e) }
rule single_quoted() -> Option<char>
= unescaped() / DQUOTE() { Some('"') } / "\\" SQUOTE() { Some('\'') } / "\\" e:escapable() { Some(e) }
rule escapable() -> char
= "b" { '\x08' }
/ "f" { '\x0c' }
/ "n" { '\n' }
/ "r" { '\r' }
/ "t" { '\t' }
/ "/" { '/' }
/ "\\" { '\\' }
/ h:("u" h:hexchar() { h }) { h }
rule hexchar() -> char
=
"{" hex:$("0"+ hexscalar()? / hexscalar()) "}"
{
char::try_from(
u32::from_str_radix(hex, 16)
.expect("Syntax ensures this works")
)
.expect("Syntax rules out surrogate sequences and numbers beyond Unicode specification")
}
/ hex:$(non_surrogate())
{
char::try_from(
u32::from(
u16::from_str_radix(hex, 16)
.expect("Syntax ensures this works")
)
)
.expect("Syntax rules out surrogate sequences and numbers beyond Unicode specification")
}
/ hl:(h:$(high_surrogate()) "\\" "u" l:$(low_surrogate()) { format!("{h}{l}") } )
{
encoding_rs::UTF_16BE.decode(
&u32::from_str_radix(&hl, 16)
.expect("Syntax ensures this works")
.to_be_bytes()
)
.0
.chars()
.next()
.expect("Syntax ensures this produces exactly one valid character")
}
rule non_surrogate()
= ((DIGIT() / "A"/"B"/"C" / "E"/"F" / "a"/"b"/"c" / "e"/"f") HEXDIG()*<3,3>)
/ (("D" / "d") ODIGIT() HEXDIG()*<2,2> )
rule high_surrogate()
= ("D" / "d") ("8"/"9"/"A"/"B"/"a"/"b") HEXDIG()*<2,2>
rule low_surrogate()
= ("D" / "d") ("C"/"D"/"E"/"F" / "c"/"d"/"e"/"f") HEXDIG()*<2,2>
rule hexscalar()
= "10" HEXDIG()*<4,4> / HEXDIG1() HEXDIG()*<4,4> / non_surrogate() / HEXDIG()*<1,3>
rule unescaped() -> Option<char> = "\r" { None } / good:[ '\x0a' | '\x0D' | '\x20'..='\x21' | '\x23'..='\x26' | '\x28'..='\x5b' | '\x5d'..='\u{d7ff}' | '\u{e000}'..='\u{10ffff}' ] { Some(good) }
rule DQUOTE() = "\""
rule SQUOTE() = "'"
rule DIGIT() = quiet!{['0'..='9']} / expected!("digits")
rule DIGIT1() = quiet!{['1'..='9']} / expected!("digits excluding 0")
rule ODIGIT() = ['0'..='7']
rule BDIGIT() = ['0'..='1']
rule HEXDIG() -> u8 = n:$(DIGIT() / ['A'..='F' | 'a'..='f']) { u8::from_str_radix(n, 16).expect("Syntax ensures this is OK") }
rule HEXDIG1() = DIGIT1() / ['A'..='F' | 'a'..='f']
rule lcalpha() = ['a'..='z']
rule lcalnum() = ['a'..='z'] / DIGIT()
rule ucalpha() = ['A'..='Z']
rule ucalnum() = ['A'..='Z'] / DIGIT()
rule wordchar() = "_" / lcalnum() / ucalpha()
pub rule app_string_h() -> Vec<u8> = S() byte:(high:HEXDIG() S() low:HEXDIG() S() { (high << 4) | low } / ellipsis() S() {? Err("Hex string was abbreviated") })*
("#" non_lf()*)?
{ byte }
rule with_slice<T>(r: rule<T>) -> (T, &'input str)
= value:&r() input:$(r()) { (value, input) }
}}