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}