cbor_tools/lib.rs
1//! `cbor-tools` is a toolkit for manipulating CBOR-encoded data.
2//!
3//! **CBOR** is a data serialization format described in [RFC7049].
4//! CBOR is a binary-friendly self-describing data encoding that has
5//! built-in types for:
6//! - Integers and Floating point numbers
7//! - Arrays and Maps
8//! - Arbitrary-length UTF-8 text strings
9//! - Arbitrary-length bytestrings
10//!
11//! Other crates (i.e. `serde_cbor`) provide `serde` serialization and
12//! deserialization of native Rust data structures.
13//!
14//! This crate provides tools for constructing and deconstructing CBOR
15//! with fine-grained control, including:
16//! - indefinite-length encoding
17//! - non-canonical encoding of integers
18//! - tagged types
19//! - sequences that may fail in strict-mode decoders
20//! - malformed sequences (for testing decoders, perhaps)
21//! - `Display` of low-level CBOR-encoded data
22//!
23//! To encode some data in CBOR, create one or more [`CborType`] values,
24//! and then call [`encode()`] on them:
25//!
26//! ```
27//! use cbor_tools::{CborType, Encode};
28//!
29//! let my_data = vec![1, 2, 3];
30//! let cbor_tree = CborType::from(my_data);
31//! let cbor_bytes = cbor_tree.encode();
32//! // cbor_bytes is a Vec<u8>
33//! ```
34//!
35//! There is a `From<T>` implementation available for many simple types.
36//! Additional data structures can be built by hand, like this non-homogenous
37//! array:
38//!
39//! ```
40//! use cbor_tools::{CborType, Encode};
41//!
42//! // An array containing a string and an integer.
43//! let list = vec![
44//! CborType::from("abc"),
45//! CborType::from(123),
46//! ];
47//! let cbor_tree = CborType::from(list);
48//! let cbor_bytes = cbor_tree.encode();
49//! // cbor_bytes is a Vec<u8>
50//! # assert_eq!(cbor_bytes, [0x82, 0x63, 0x61, 0x62, 0x63, 0x18, 0x7b]);
51//! ```
52//!
53//! Decoding of arbitrary CBOR data can be performed using the [`Decode`]
54//! trait.
55//!
56//! To examine the low-level details of CBOR-encoded data, use the
57//! [`DecodeSymbolic`] trait, which optionally implements `Display`
58//! if the `display` feature is enabled.
59//!
60//!
61//! [RFC7049]: https://tools.ietf.org/html/rfc7049
62//! [`encode()`]: Encode::encode
63//!
64
65#![warn(missing_docs)]
66#![forbid(unsafe_code)]
67#![warn(clippy::cast_possible_truncation)]
68
69use half::f16;
70use std::convert::TryInto;
71use std::fmt;
72use std::{convert::TryFrom, ops::Deref};
73
74use crate::truncate::{Truncate, TruncateFrom};
75
76#[cfg(feature = "display")]
77mod display;
78/// Specifies the exact binary format of CBOR data.
79pub mod format;
80#[doc(hidden)]
81pub mod test_util;
82mod truncate;
83
84/// CBOR Integer type
85///
86/// ```
87/// use cbor_tools::{CborType, Integer, Encode};
88///
89/// let val = Integer::from(123);
90/// // This is the CBOR encoding for an 8-bit integer.
91/// assert_eq!(CborType::from(val).encode(), vec![0x18, 0x7B]);
92/// ```
93///
94/// CBOR integers will be represented in "canonical" form if the
95/// `From<u8 | u32 | u64>` impls are used. Non-canonical forms can
96/// be created by initializing the struct directly:
97/// ```
98/// # use cbor_tools::{CborType, Integer, Encode};
99/// let val = Integer::U32(100);
100/// assert_eq!(CborType::from(val).encode(), vec![0x1a, 0, 0, 0, 0x64]);
101/// ```
102///
103/// Note: integers outside the range of [-2^64, 2^64-1] should be encoded
104/// as byte strings instead.
105#[derive(Clone, PartialEq)]
106pub enum Integer {
107 /// A value between 0 and 23.
108 U5(ZeroTo23), // FIXME: use some bit-field crate?
109 /// An 8-bit non-negative integer.
110 U8(u8),
111 /// A 16-bit non-negative integer.
112 U16(u16),
113 /// A 32-bit non-negative integer.
114 U32(u32),
115 /// A 64-bit non-negative integer
116 U64(u64),
117 // Because negative integers have a broader range than
118 // signed integers, they are pre-encoded as a negative
119 // offset from -1.
120 // FIXME: need a custom Debug implementation that understands
121 // the offset, otherwise this will be confusing.
122 /// A small negative integer (between -1 and -24)
123 N5(ZeroTo23),
124 /// An 8-bit negative integer.
125 N8(u8),
126 /// A 16-bit negative integer.
127 N16(u16),
128 /// A 32-bit negative integer.
129 N32(u32),
130 /// A 64-bit negative integer.
131 N64(u64),
132}
133
134// A crutch to assist with debug-printing of negative integers,
135// which are stored in an inconvenient format.
136struct DebugNeg<T: fmt::Debug>(T);
137
138impl<T> fmt::Debug for DebugNeg<T>
139where
140 T: fmt::Debug + Copy + Into<u128>,
141{
142 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
143 // CBOR negative values are an offset from -1.
144 let x = 1u128 + self.0.into();
145 write!(formatter, "-{:?}", x)
146 }
147}
148
149impl fmt::Debug for Integer {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 let mut builder = f.debug_struct("Integer");
152 match self {
153 Integer::U5(x) => {
154 builder.field("U5", &x.0);
155 }
156 Integer::U8(x) => {
157 builder.field("U8", &x);
158 }
159 Integer::U16(x) => {
160 builder.field("U16", &x);
161 }
162 Integer::U32(x) => {
163 builder.field("U32", &x);
164 }
165 Integer::U64(x) => {
166 builder.field("U64", &x);
167 }
168 Integer::N5(x) => {
169 let x: u8 = **x;
170 builder.field("N5", &DebugNeg(x));
171 }
172 Integer::N8(x) => {
173 builder.field("N8", &DebugNeg(*x));
174 }
175 Integer::N16(x) => {
176 builder.field("N16", &DebugNeg(*x));
177 }
178 Integer::N32(x) => {
179 builder.field("N32", &DebugNeg(*x));
180 }
181 Integer::N64(x) => {
182 builder.field("N64", &DebugNeg(*x));
183 }
184 }
185 builder.finish()
186 }
187}
188
189/// An integer value in the range 0 to 23, inclusive.
190#[derive(Copy, Clone, PartialEq)]
191pub struct ZeroTo23(u8);
192
193impl From<u8> for ZeroTo23 {
194 /// Create a new ZeroTo23.
195 ///
196 /// Will panic if the input is outside the expected range.
197 fn from(x: u8) -> Self {
198 if x >= 24 {
199 panic!("too big for ZeroTo23::new()");
200 }
201 ZeroTo23(x)
202 }
203}
204
205impl From<i8> for ZeroTo23 {
206 /// Create a new ZeroTo23.
207 ///
208 /// Will panic if the input is outside the expected range.
209 fn from(x: i8) -> Self {
210 let x: u8 = x.try_into().unwrap();
211 if x >= 24 {
212 panic!("too big for ZeroTo23::new()");
213 }
214 ZeroTo23(x)
215 }
216}
217
218impl<I> From<I> for ZeroTo23
219where
220 I: Truncate<u8>,
221{
222 /// Create a new ZeroTo23.
223 ///
224 /// Will panic if the input is outside the expected range.
225 fn from(x: I) -> Self {
226 let x: u8 = x.truncate();
227 ZeroTo23::from(x)
228 }
229}
230
231impl Deref for ZeroTo23 {
232 type Target = u8;
233
234 /// Extract the integer value.
235 ///
236 /// Will panic if the stored value is outside the expected range.
237 fn deref(&self) -> &Self::Target {
238 if self.0 >= 24 {
239 panic!("ZeroTo23 is out of range");
240 }
241 &self.0
242 }
243}
244
245impl From<u8> for Integer {
246 fn from(x: u8) -> Self {
247 if x < 24 {
248 Integer::U5(ZeroTo23::from(x))
249 } else {
250 Integer::U8(x)
251 }
252 }
253}
254
255impl From<u16> for Integer {
256 fn from(x: u16) -> Self {
257 if x < 24 {
258 Integer::U5(ZeroTo23::from(x))
259 } else if x < 0x100 {
260 Integer::from(x.truncate())
261 } else {
262 Integer::U16(x)
263 }
264 }
265}
266
267impl From<u32> for Integer {
268 fn from(x: u32) -> Self {
269 if x < 24 {
270 Integer::U5(ZeroTo23::from(x))
271 } else if x < 0x100 {
272 Integer::U8(x.truncate())
273 } else if x < 0x10000 {
274 Integer::U16(x.truncate())
275 } else {
276 Integer::U32(x)
277 }
278 }
279}
280
281impl From<u64> for Integer {
282 fn from(x: u64) -> Self {
283 if x < 24 {
284 Integer::U5(ZeroTo23::from(x))
285 } else if x < 0x100 {
286 Integer::U8(x.truncate())
287 } else if x < 0x10000 {
288 Integer::U16(x.truncate())
289 } else if x < 0x100000000 {
290 Integer::U32(x.truncate())
291 } else {
292 Integer::U64(x)
293 }
294 }
295}
296
297impl From<i8> for Integer {
298 fn from(x: i8) -> Self {
299 if x >= 0 {
300 Integer::from(x as u8)
301 } else if x > -25 {
302 Integer::N5(ZeroTo23::from(-1 - x))
303 } else {
304 Integer::N8((-1 - x) as u8)
305 }
306 }
307}
308
309impl From<i16> for Integer {
310 fn from(x: i16) -> Self {
311 if x >= 0 {
312 Integer::from(x as u16)
313 } else if x > -25 {
314 // Should never overflow, but panic if it does.
315 //let val: u8 = (-1 - x).try_into().unwrap();
316 Integer::N5(ZeroTo23::from(-1 - x))
317 } else if x > -0x101 {
318 // Should never overflow, but panic if it does.
319 let val: u8 = (-1 - x).try_into().unwrap();
320 Integer::N8(val)
321 } else {
322 Integer::N16((-1 - x) as u16)
323 }
324 }
325}
326
327impl From<i32> for Integer {
328 fn from(x: i32) -> Self {
329 if x >= 0 {
330 Integer::from(x as u32)
331 } else if x > -25 {
332 Integer::N5(ZeroTo23::from(-1 - x))
333 } else if x > -0x101 {
334 Integer::N8((-1 - x).truncate())
335 } else if x > -0x10001 {
336 Integer::N16((-1 - x).truncate())
337 } else {
338 Integer::N32((-1 - x) as u32)
339 }
340 }
341}
342
343impl From<i64> for Integer {
344 fn from(x: i64) -> Self {
345 if x >= 0 {
346 Integer::from(x as u64)
347 } else if x > -25 {
348 Integer::N5(ZeroTo23::from(-1 - x))
349 } else if x > -0x101 {
350 Integer::N8((-1 - x).truncate())
351 } else if x > -0x10001 {
352 Integer::N16((-1 - x).truncate())
353 } else if x > -0x100000001 {
354 Integer::N32((-1 - x).truncate())
355 } else {
356 Integer::N64((-1 - x) as u64)
357 }
358 }
359}
360
361/// The integer value was too large to be represented as a CBOR integer.
362///
363/// In CBOR, only 64-bit positive and negative integers are supported.
364/// If your integer is out of this range, use a byte-stream instead.
365#[derive(Clone, Copy, Debug)]
366pub struct IntOverflowError;
367
368impl TryFrom<i128> for Integer {
369 type Error = IntOverflowError;
370
371 fn try_from(value: i128) -> Result<Self, Self::Error> {
372 if value > u64::MAX as i128 {
373 Err(IntOverflowError)
374 } else if value > 0 {
375 Ok(Integer::from(u64::truncate_from(value)))
376 } else if value > i64::MIN as i128 {
377 Ok(Integer::from(i64::truncate_from(value)))
378 } else if value < -18446744073709551616 {
379 // won't fit in 64 bits after offset from -1
380 Err(IntOverflowError)
381 } else {
382 // transform to offset from -1
383 let nvalue = -1 - value;
384 // Value is negative, and is outside the range
385 // that i64 can store. So we know that the
386 // canonical representation needs 8 bytes.
387 Ok(Integer::N64(nvalue.truncate()))
388 }
389 }
390}
391
392impl From<f16> for Float {
393 fn from(x: f16) -> Self {
394 Float::F16(x)
395 }
396}
397
398impl From<f32> for Float {
399 fn from(x: f32) -> Self {
400 let x16 = f16::from_f32(x);
401 let x32 = f32::from(x16);
402 if x32 == x {
403 Float::from(x16)
404 } else {
405 Float::F32(x)
406 }
407 }
408}
409
410impl From<f64> for Float {
411 fn from(x: f64) -> Self {
412 // Attempt to truncate the value to see if it's the same value.
413 #[allow(clippy::cast_possible_truncation)]
414 let x32 = x as f32;
415 let x64 = x32 as f64;
416 if x64 == x {
417 Float::from(x32)
418 } else {
419 Float::F64(x)
420 }
421 }
422}
423
424/// A byte string.
425///
426/// A "byte string" in CBOR is an arbitrary length array of bytes.
427///
428/// ```
429/// use cbor_tools::{CborType, ByteString, Encode};
430///
431/// let bytes = [1u8, 2, 3, 4];
432/// let val = ByteString::from(&bytes[..]);
433/// assert_eq!(CborType::from(val).encode(), vec![0x44, 1, 2, 3, 4]);
434/// ```
435///
436#[derive(Debug, Clone, PartialEq)]
437pub struct ByteString(pub Vec<u8>);
438
439impl<T> From<T> for ByteString
440where
441 T: Into<Vec<u8>>,
442{
443 // Create a definite-length byte string.
444 fn from(b: T) -> Self {
445 ByteString(b.into())
446 }
447}
448
449/// A UTF-8 text string.
450///
451/// ```
452/// use cbor_tools::{CborType, TextString, Encode};
453///
454/// let name = "Foo!";
455/// let val = TextString::from(name);
456/// assert_eq!(CborType::from(val).encode(), vec![0x64, 0x46, 0x6f, 0x6f, 0x21]);
457/// ```
458///
459#[derive(Debug, Clone, PartialEq)]
460pub struct TextString(pub String);
461
462impl<T> From<T> for TextString
463where
464 T: Into<String>,
465{
466 // Create a definite-length string.
467 fn from(s: T) -> Self {
468 TextString(s.into())
469 }
470}
471
472/// An array of values.
473///
474/// In CBOR, arrays may have members of different types.
475///
476/// Use `Array::from()` to construct an array.
477///
478/// ```
479/// # use cbor_tools::{CborType, Array, Encode};
480/// let nums = vec![1, 2, 3];
481/// let array = Array::from(nums);
482/// assert_eq!(CborType::from(array).encode(), vec![0x83, 1, 2, 3]);
483/// ```
484#[derive(Debug, Clone, PartialEq)]
485pub struct Array(pub Vec<CborType>);
486
487impl<T> From<Vec<T>> for Array
488where
489 T: Into<CborType>,
490{
491 fn from(v: Vec<T>) -> Self {
492 Array(v.into_iter().map(|x| x.into()).collect())
493 }
494}
495
496/// An map of (key, value) pairs.
497///
498/// In CBOR, each key and value may be of different types.
499///
500/// Use `Map::from()` to construct a map.
501///
502/// ```
503/// # use cbor_tools::{CborType, Map, Encode};
504/// let map_pairs = vec![(1, 2), (3, 4)];
505/// let map = Map::from(map_pairs);
506/// assert_eq!(CborType::from(map).encode(), vec![0xa2, 1, 2, 3, 4]);
507/// ```
508///
509#[derive(Debug, Clone, PartialEq)]
510pub struct Map(Vec<(CborType, CborType)>);
511
512impl<K, V> From<Vec<(K, V)>> for Map
513where
514 K: Into<CborType>,
515 V: Into<CborType>,
516{
517 fn from(v: Vec<(K, V)>) -> Self {
518 Map(v.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
519 }
520}
521
522/// A floating-point value.
523///
524/// Use `Float::from()` to construct a `Float`.
525#[derive(Debug, Clone, PartialEq)]
526pub enum Float {
527 /// IEEE 754 Half-Precision Float (16 bits)
528 F16(f16),
529 /// IEEE 754 Single-Precision Float (32 bits)
530 F32(f32),
531 /// IEEE 754 Double-Precision Float (64 bits)
532 F64(f64),
533}
534
535/// A tagged value.
536///
537/// A Tagged value contains a numeric tag, which specifies
538/// some additional information, and a payload value, which
539/// may be of any type (though some tags are only expected
540/// to be used with particular types).
541///
542/// Use [`Tag::wrap`] to create a `Tagged`.
543#[derive(Debug, Clone, PartialEq)]
544pub struct Tagged {
545 tag: Tag,
546 child: Box<CborType>,
547}
548
549/// A tag value for use with [`Tagged`].
550///
551/// Tags are just integers; this type exists to avoid any
552/// type confusion when passing them as function arguments.
553///
554/// See RFC 7049 2.4 for details.
555#[derive(Debug, Copy, Clone, PartialEq)]
556pub struct Tag(u64);
557
558#[allow(missing_docs)]
559impl Tag {
560 pub const STD_DATE_TIME: Tag = Tag(0);
561 pub const EPOCH_DATE_TIME: Tag = Tag(1);
562 pub const POS_BIGNUM: Tag = Tag(2);
563 pub const NEG_BIGNUM: Tag = Tag(3);
564 pub const DECIMAL_FRACTION: Tag = Tag(4);
565 pub const BIGFLOAT: Tag = Tag(5);
566 pub const EXPECT_BASE64URL: Tag = Tag(21);
567 pub const EXPECT_BASE64: Tag = Tag(22);
568 pub const EXPECT_BASE16: Tag = Tag(23);
569 pub const CBOR_DATA: Tag = Tag(24);
570 pub const URI: Tag = Tag(32);
571 pub const BASE64URL: Tag = Tag(33);
572 pub const BASE64: Tag = Tag(34);
573 pub const REGEXP: Tag = Tag(35);
574 pub const MIME: Tag = Tag(36);
575 pub const SELF_DESC_CBOR: Tag = Tag(55799);
576}
577
578impl Tag {
579 /// Use the [`Tag`] value to create a new [`Tagged`] struct.
580 ///
581 /// This saves a little typing. Instead of:
582 /// ```compile_fail
583 /// # // doesn't build; there is no `new` fn.
584 /// # use cbor_tools::{CborType, Tag, Tagged};
585 /// # let bytestring = CborType::from(&[0u8; 12][..]);
586 /// Tagged::new(Tag::POS_BIGNUM, bytestring)
587 /// # ;
588 /// ```
589 /// one can instead type:
590 /// ```
591 /// # use cbor_tools::{CborType, Tag};
592 /// # let bytestring = CborType::from(&[0u8; 12][..]);
593 /// Tag::POS_BIGNUM.wrap(bytestring)
594 /// # ;
595 ///```
596 pub fn wrap(self, child: CborType) -> Tagged {
597 Tagged {
598 tag: self,
599 child: Box::new(child),
600 }
601 }
602}
603
604/// A CBOR value.
605///
606/// This enum can represent any CBOR value; see the documentation
607/// of each variant for more details.
608///
609/// Many variants can be constructed directly using `from()`.
610/// For example,
611/// ```
612/// # use cbor_tools::CborType;
613/// let i = 42;
614/// let x = CborType::from(i);
615/// ```
616/// produces the same value as
617/// ```
618/// # use cbor_tools::{CborType, Integer};
619/// let i = 42;
620/// let x = CborType::Integer(Integer::from(i));
621/// ```
622#[derive(Debug, Clone, PartialEq)]
623#[allow(missing_docs)]
624pub enum CborType {
625 Null,
626 Undefined,
627 Bool(bool),
628 Integer(Integer),
629 ByteString(ByteString),
630 TextString(TextString),
631 Array(Array),
632 Map(Map),
633 Indefinite(Indefinite),
634 Tagged(Tagged),
635 Float(Float),
636}
637
638/// Indefinite-length bytestrings, textstrings, arrays, and maps.
639#[derive(Debug, Clone, PartialEq)]
640#[allow(missing_docs)]
641pub enum Indefinite {
642 ByteString(Vec<ByteString>),
643 TextString(Vec<TextString>),
644 Array(Array),
645 Map(Map),
646}
647
648impl<T> From<T> for CborType
649where
650 T: Into<Integer>,
651{
652 fn from(x: T) -> Self {
653 CborType::Integer(x.into())
654 }
655}
656
657impl From<f16> for CborType {
658 fn from(x: f16) -> Self {
659 CborType::Float(x.into())
660 }
661}
662
663impl From<f32> for CborType {
664 fn from(x: f32) -> Self {
665 CborType::Float(x.into())
666 }
667}
668
669impl From<f64> for CborType {
670 fn from(x: f64) -> Self {
671 CborType::Float(x.into())
672 }
673}
674
675impl From<&str> for CborType {
676 fn from(x: &str) -> Self {
677 CborType::TextString(x.into())
678 }
679}
680
681impl From<&[u8]> for CborType {
682 fn from(x: &[u8]) -> Self {
683 CborType::ByteString(x.into())
684 }
685}
686
687impl From<ByteString> for CborType {
688 fn from(x: ByteString) -> CborType {
689 CborType::ByteString(x)
690 }
691}
692
693impl From<TextString> for CborType {
694 fn from(x: TextString) -> CborType {
695 CborType::TextString(x)
696 }
697}
698
699impl From<Array> for CborType {
700 fn from(x: Array) -> CborType {
701 CborType::Array(x)
702 }
703}
704
705impl From<Map> for CborType {
706 fn from(x: Map) -> CborType {
707 CborType::Map(x)
708 }
709}
710
711impl<T> From<Vec<T>> for CborType
712where
713 T: Into<CborType>,
714{
715 fn from(x: Vec<T>) -> CborType {
716 let list: Vec<CborType> = x.into_iter().map(|i| i.into()).collect();
717 CborType::Array(Array(list))
718 }
719}
720
721impl<K, V> From<Vec<(K, V)>> for CborType
722where
723 K: Into<CborType>,
724 V: Into<CborType>,
725{
726 fn from(x: Vec<(K, V)>) -> CborType {
727 let list: Vec<(CborType, CborType)> =
728 x.into_iter().map(|(k, v)| (k.into(), v.into())).collect();
729 CborType::Map(Map(list))
730 }
731}
732
733#[doc(hidden)]
734pub trait Canonical {
735 fn is_canonical(&self) -> bool;
736 fn to_canonical(&self) -> Self; // or Cow<Self> ?
737}
738
739/// Binary CBOR encoding.
740pub trait Encode {
741 /// Encode data to bytes.
742 fn encode(&self) -> Vec<u8>;
743}
744
745/// Symbolic CBOR encoding.
746pub trait EncodeSymbolic {
747 /// Encode data to [`format::Element`] symbols representing a CBOR encoding.
748 fn encode_symbolic(&self) -> Vec<format::Element>;
749}
750
751/// An error that may occur when decoding CBOR Data.
752#[derive(Clone, Copy, Debug, PartialEq)]
753pub enum DecodeError {
754 /// No more CBOR items are available in the input data.
755 End,
756 /// Not enough bytes were available to complete decoding.
757 Underrun,
758 /// A CBOR text string contains invalid UTF-8 data.
759 Utf8Error,
760 /// The byte sequence cannot be decoded as CBOR data.
761 Undecodable,
762 /// Improper nesting of types inside an indefinite text or byte string.
763 BadSubString,
764 /// CBOR elements were terminated by a BREAK symbol.
765 Break,
766 /// CBOR element was marked as indefinite-length.
767 Indefinite,
768 /// A Map didn't have an even number of members.
769 MapPairError,
770 /// An unknown Simple Value was encountered.
771 UnknownSimple(u8),
772}
773
774/// Binary CBOR encoding.
775pub trait Decode {
776 /// Decode data to a [`CborType`] list.
777 fn decode(&self) -> Result<Vec<CborType>, DecodeError>;
778}
779
780/// Symbolic CBOR decoding.
781pub trait DecodeSymbolic {
782 /// Decode data to a low-level [`format::Element`] list.
783 fn decode_symbolic(&self) -> Result<Vec<format::Element>, DecodeError>;
784}
785
786impl EncodeSymbolic for Vec<CborType> {
787 fn encode_symbolic(&self) -> Vec<format::Element> {
788 self.iter()
789 .map(EncodeSymbolic::encode_symbolic)
790 .flatten()
791 .collect()
792 }
793}
794
795impl Encode for Vec<CborType> {
796 fn encode(&self) -> Vec<u8> {
797 self.iter()
798 .map(|x| x.encode_symbolic().encode())
799 .flatten()
800 .collect()
801 }
802}
803
804impl Encode for CborType {
805 fn encode(&self) -> Vec<u8> {
806 self.encode_symbolic().encode()
807 }
808}