Using dCBOR

So after all that discussion of the motivation for dCBOR, let's just recap its rules all in one place, and specifically how they differ from basic CBOR:

  • Map Keys: No duplicates. Must be serialized sorted lexicographically by the serialized key.
  • Numeric Values: "Preferred Serialization" isn't just preferred, it's required.
  • Numeric Reduction: Floating point values that can accurately be represented as integers must be serialized as integers.
  • Indefinite Length: Indefinite length values are not allowed.
  • Simple Values: Only false, true, and null are allowed.
  • Strings: Must be encoded in Unicode Normalization Form C (NFC).
  • Decoders: Must check all the rules above and reject any serialization that doesn't conform to them.

Pretty simple, right?

It gets even simpler when you use a CBOR library that supports dCBOR directly, as the implementation should take care of all the details for you. In fact, a good API will even make it impossible to create invalid dCBOR serializations.

The dcbor crate is the Rust reference implementation of dCBOR from Blockchain Commons, and in this chapter we'll show you how easy it is to use.

Installation

This will add the latest version of the dcbor crate to your Cargo.toml file:

cargo add dcbor

Getting Started

dcbor includes a prelude module that re-exports all the types and traits you need to use dCBOR:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Many common types are directly convertible into dCBOR. Thanks to dCBOR's numeric reduction, you don't even need to specify whether common numeric types should be serialized as integers or floating point: the dcbor library will automatically choose the best representation for you.

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

When you use value.to_cbor() or CBOR::from(value), you're not actually encoding the CBOR serialization in that moment. You're actually creating an intermediate representation of the data (an instance of CBOR) that can be serialized later when you call a method like to_cbor_data.

Converting back from CBOR is also easy: you specify the type you want to convert to, and the dcbor library will do the rest. You use the try_from method to convert from CBOR to a Rust type, which will succeed if the CBOR can be accurately converted to that type. If the conversion fails, it will return an error:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

In the following example we use try_from to convert from CBOR to both a u8 type and an f64 type. Both succeed, because the value 42 can be represented as both an 8-bit unsigned integer and a 64-bit floating point number:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

✅ NOTE: Observe the call to clone() above, which we need because the try_from method consumes the CBOR value, and we still need an instance for the second try_from call. Instances of CBOR are immutable, and the dcbor library implements structure sharing, so cloning is always cheap.

Below we encode a floating point value with a non-zero fractional part, which succeeds in being decoded back to floating point but fails to decode back to an integer, because precision would be lost:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

This idiom is not just for numeric types: you can use it for any type that implements the TryFrom<CBOR> trait, like String:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

It even works for vectors:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Byte Strings

The last example raises an interesting question: is our Vec<u8> being serialized as a CBOR array or a CBOR byte string? Let's check:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

As you can see, the header byte specifies an array of five elements, followed by five CBOR data items for the integers 1, 2, 3, 4, and 5. So the Vec<u8> is being serialized as a CBOR array, not a byte string.

In Rust, Vec<u8> is often used to represent a string or buffer of bytes, but in CBOR, a byte string is a different type distinct from a vector or an array. The CBOR type provides a static method CBOR::to_byte_string that converts a Vec<u8> into a CBOR byte string:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Everything in the serialization in this example is the same as the last, except the header byte, which was 0x85 for a 5-element array, and 0x45 for a byte string of length 5.

Notice that recovering the byte string is also different. Since a byte string is not an array, we can't extract it as a Vec<u8>. Instead, we extract it as the type ByteString, and then convert that to a Vec<u8> using .into().

ByteString is just a wrapper around Vec<u8>, and it has most of the same capabilities, but the dcbor library treats it as a CBOR byte string, not a CBOR array.

Simple Values: false, true, and null

dCBOR supports three simple values— false, true, and null— and the dcbor library provides a set of conveniences for working with them. In the example below we create a CBOR array containing [true, false, null], and then test its CBOR diagnostic notation and annotated hex serialization:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Something interesting is going on here: our array has three values, two of which are booleans and the third is its own type: null. CBOR is designed to handle such heterogeneous arrays with no problem. But Rust (unlike some languages like JavaScript) doesn't have a null value (preferring Option<T> for values which may not be present). Rust also doesn't natively support Vecs containing mixed types. So how does the dcbor library handle this?

First, note that our array is not declared as a Vec<bool> but as a Vec<CBOR>. The CBOR type can hold any CBOR value, including complex values like nested arrays and maps. In the context of the vec! macro composing a Vec<CBOR>, the Rust boolean values true and false can just be converted directly using .into(), and that's what we're doing here.

Rust has no null value, so the dcbor library provides a CBOR::null() method that returns a CBOR instance representing the null value.

And since all three elements of the array are being converted directly into CBOR, there is no problem constructing the heterogeneous array.

✅ NOTE: Of course, dCBOR doesn't support CBOR undefined or any of the other simple values, so the dcbor API doesn't have ways to let you construct them.

Extracting from a Heterogeneous Array

So now that we've gotten ourselves into this situation, how do we get the values back out? The dcbor library provides a set of methods for testing and extracting the CBOR major types, as well as unique values like true, false, and null:

In the example below we first begin by extracting our CBOR array from the composed CBOR instance. We then demonstrate several methods to either extract values or test them against expected values.

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Maps

As long as all the types contained in a Rust HashMap or BTreeMap are supported by CBOR (we'll discuss how to make your own types CBOR-compatible in a later chapter), then converting them to CBOR and back is straightforward.

In the example below we round-trip a Rust HashMap with String keys and Vec<String> values all the way to serialized CBOR data and back again:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Those familiar with JSON know that it only supports string keys, but CBOR supports any type of CBOR value as a key, and it's a common pattern to use integers as keys, which are much more compact:

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

Note the use of diagnostic_flat() in this example, which returns the diagnostic notation with no line breaks or indentation. In previous examples we also used either hex() or hex_annotated() depending on the desired formatting.

Heterogeneous Maps

CBOR (and the dcbor library) supports heterogeneous maps, which means that the keys and values can be of different types within the same map. The technique is basically the same as with heterogeneous arrays: you use CBOR as the type for the keys and values, and then convert them to the appropriate types when you extract them.

use anyhow::Result;
use std::{collections::HashMap, vec};

// This is all you need to import to use the library.
use dcbor::prelude::*;

#[rustfmt::skip]
pub fn main() {
    // Encode the integer 42
    let i = 42;
    let cbor: CBOR = i.to_cbor();
    // The CBOR type above here for clarity, can be inferred

    // Check the diagnostic representation
    assert_eq!(cbor.diagnostic(), "42");

    // Check the hex representation
    assert_eq!(cbor.hex(), "1a002a");

    // Check the CBOR data
    assert_eq!(cbor.to_cbor_data(), vec![0x1a, 0x00, 0x2a]);
}

#[test]
#[rustfmt::skip]
fn test_2() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();
let b = i32::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_3() -> Result<()> {
let a = 42;
let cbor = a.to_cbor();

// Decode as a u8
let b = u8::try_from_cbor(&cbor)?;
assert_eq!(a as u8, b);

// Decode as an f64
let c = f64::try_from_cbor(&cbor)?;
assert_eq!(a as f64, c);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_4() -> Result<()> {
let a = 1.23456;
let cbor = a.to_cbor();

// Decode as an f64
let b = f64::try_from_cbor(&cbor)?;
assert_eq!(a, b);

// Cannot decode as a i32
assert!(i32::try_from_cbor(&cbor).is_err());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_5() -> Result<()> {
let a = "Hello, dCBOR!";
let cbor = a.to_cbor();

// Decode as an f64 fails
assert!(f64::try_from_cbor(&cbor).is_err());

// Decode as a String succeeds
let b = String::try_from_cbor(&cbor)?;
assert_eq!(a, b);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_6() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

// Decode as Vec of a compatible type: 32-bit signed integers
let b: Vec<i32> = Vec::try_from_cbor(&cbor)?;
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_7() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a: Vec<u8> = vec![1, 2, 3, 4, 5];
let cbor = a.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

85      # array(5)
    01  # unsigned(1)
    02  # unsigned(2)
    03  # unsigned(3)
    04  # unsigned(4)
    05  # unsigned(5)

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_8() -> Result<()> {
// Encode a vector of 8-bit unsigned integers
let a = vec![1, 2, 3, 4, 5];
let byte_string = CBOR::to_byte_string(a);
let cbor = byte_string.to_cbor();

let hex = cbor.hex_annotated();
let expected_hex = r#"

45              # bytes(5)
    0102030405

"#.trim();

assert_eq!(hex, expected_hex);

let b: Vec<u8> = ByteString::try_from_cbor(&cbor)?.into();
assert_eq!(b, vec![1, 2, 3, 4, 5]);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_9() -> Result<()> {
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
let cbor = v.to_cbor();

let diagnostic = cbor.diagnostic();
let expected_diagnostic = "[true, false, null]";
assert_eq!(diagnostic, expected_diagnostic);

let hex = cbor.hex_annotated();
let expected_hex = r#"

83      # array(3)
    f5  # true
    f4  # false
    f6  # null

"#.trim();

assert_eq!(hex, expected_hex);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_10() -> Result<()> {
// Compose an array of CBOR values
let v: Vec<CBOR> = vec![
    true.into(),
    false.into(),
    CBOR::null(),
];
// Convert the array to a single CBOR object, which would
// be serialized to CBOR data or recovered from it.
let cbor: CBOR = v.to_cbor();

// Recover the array from the CBOR object
let v2: Vec<CBOR> = CBOR::try_array(&cbor)?;

// Check the length of the array
assert_eq!(v2.len(), 3);

// For the first value (`true`), extract it so it could be saved for later.
let t = CBOR::try_bool(&v2[0])?;
assert!(t);

// For the second value (`false`), just assert that it is false.
assert!(v2[1].is_false());

// For the third value (`null`), assert that it is null.
assert!(v2[2].is_null());
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_11() -> Result<()> {
// Create a HashMap with String keys and Vec<String> values
let mut h: HashMap<String, Vec<String>> = HashMap::new();
h.insert("animals".into(), vec!("cat".into(), "dog".into(), "horse".into()));
h.insert("colors".into(), vec!["red".into(), "green".into(), "blue".into()]);

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic();
let expected_diagnostic = r#"

{
    "colors":
    ["red", "green", "blue"],
    "animals":
    ["cat", "dog", "horse"]
}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Serialize the CBOR to binary data
let data: Vec<u8> = cbor.to_cbor_data();

// Check the hex representation of the serialized data
let hex = hex::encode(&data);
let expected_hex = "a266636f6c6f7273836372656465677265656e64626c756567616e696d616c73836363617463646f6765686f727365";
assert_eq!(hex, expected_hex);

// Deserialize the data back into a CBOR object
let cbor2: CBOR = CBOR::try_from_data(data)?;

// Convert the CBOR object back into a HashMap
let h2: HashMap<String, Vec<String>> = cbor2.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_12() -> Result<()> {
// Create a HashMap with integer keys and Vec<String> values
let mut h: HashMap<usize, Vec<String>> = HashMap::new();
h.insert(1, ["cat", "dog", "horse"].map(str::to_string).to_vec());
h.insert(2, ["red", "green", "blue"].map(str::to_string).to_vec());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<usize, Vec<String>> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}


#[test]
#[rustfmt::skip]
fn test_13() -> Result<()> {
// Create a HashMap with CBOR for its keys and values
let mut h: HashMap<CBOR, CBOR> = HashMap::new();
h.insert(1.into(), vec![CBOR::from("cat"), "dog".into(), "horse".into()].into());
h.insert(2.into(), vec![CBOR::from("red"), "green".into(), "blue".into()].into());

// Convert the HashMap to a CBOR object
let cbor = h.to_cbor();

// Check the representation in CBOR diagnostic notation
let diagnostic = cbor.diagnostic_flat();
let expected_diagnostic = r#"

{1: ["cat", "dog", "horse"], 2: ["red", "green", "blue"]}

"#.trim();
assert_eq!(diagnostic, expected_diagnostic);

// Convert the CBOR object back into a HashMap
let h2: HashMap<CBOR, CBOR> = cbor.try_into()?;

// Check that the original and deserialized HashMaps are equal
assert_eq!(h, h2);
Ok(())
}

#[test]
#[rustfmt::skip]
fn test_14() -> Result<()> {
// number to CBOR
let n = 10;
let cbor = n.to_cbor();
// CBOR to number
assert_eq!(i32::try_from_cbor(&cbor)?, n);
assert_eq!(f64::try_from_cbor(&cbor)?, n as f64);

// bool to CBOR
let b = true;
let cbor = b.to_cbor();
// CBOR to bool
assert_eq!(bool::try_from_cbor(&cbor)?, b);
assert_eq!(CBOR::try_bool(&cbor)?, b);

// null to CBOR
// let n = CBOR::null();
// let cbor = n.to_cbor();
// // CBOR to null
// let n2 = CBOR::try_from_cbor(&cbor)?;
// assert_eq!(n2, n);
// assert!(cbor.is_null());

// bstr to CBOR
let v = vec![1, 2, 3, 4, 5];
let b = ByteString::from(&v);
let cbor = b.to_cbor();
let cbor2 = CBOR::to_byte_string(&v);
assert_eq!(cbor, cbor2);
// CBOR to bstr
assert_eq!(ByteString::try_from_cbor(&cbor)?, b);
let array: Vec<u8> = CBOR::try_byte_string(&cbor)?;
assert_eq!(array, v);

// tstr to CBOR
let t = "Hello";
let cbor = t.to_cbor();
// CBOR to tstr
assert_eq!(String::try_from_cbor(&cbor)?, t);
assert_eq!(CBOR::try_text(&cbor)?, t);

// array to CBOR
let a = vec![1, 2, 3];
let cbor = a.to_cbor();
// CBOR to homogenous array
let b = Vec::<i32>::try_from_cbor(&cbor)?;
assert_eq!(b, a);
// CBOR to heterogeneous array
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = b.iter()
    .map(|x| i32::try_from_cbor(x).map_err(Into::into))
    .collect::<Result<_>>()?;
assert_eq!(b, a);
let b: Vec<CBOR> = CBOR::try_array(&cbor)?;
let b: Vec<i32> = vec![
    i32::try_from_cbor(&b[0])?,
    i32::try_from_cbor(&b[1])?,
    i32::try_from_cbor(&b[2])?,
];
assert_eq!(b, a);

// map to CBOR
let mut m: HashMap<String, i32> = HashMap::new();
m.insert("a".into(), 1);
m.insert("b".into(), 2);
let cbor = m.to_cbor();
// CBOR to homogenous map
let m2 = HashMap::<String, i32>::try_from_cbor(&cbor)?;
assert_eq!(m, m2);
// CBOR to heterogeneous map
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let m2: HashMap<String, i32> = m2.iter()
    .map(|(k, v)| {
        let k = String::try_from_cbor(k).map_err(anyhow::Error::from)?;
        let v = i32::try_from_cbor(v).map_err(anyhow::Error::from)?;
        Ok((k, v))
    })
    .collect::<Result<_>>()?;
assert_eq!(m, m2);
let m2: dcbor::Map = CBOR::try_map(&cbor)?;
let a: i32 = m2.extract("a")?;
assert_eq!(a, 1);
let b: i32 = m2.extract("b")?;
assert_eq!(b, 2);

// tagged to CBOR
let t = CBOR::to_tagged_value(999, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(Tag::from(999), t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// tagged (with name) to CBOR
let named_tag = Tag::new(999, "my-tag");
let t = CBOR::to_tagged_value(&named_tag, "Hello");
let cbor = t.to_cbor();
// CBOR to tagged
let t2: (Tag, CBOR) = CBOR::try_tagged_value(&cbor)?;
assert_eq!(named_tag, t2.0);
assert_eq!(String::try_from_cbor(&t2.1)?, "Hello");
// Expecting a specific tag
let t2 = CBOR::try_expected_tagged_value(&cbor, &named_tag)?;
assert_eq!(String::try_from_cbor(&t2)?, "Hello");

// Registering a tag for diagnostic annotation
with_tags_mut!(|tags: &mut TagsStore| {
    tags.insert(named_tag);
});
assert_eq!(cbor.diagnostic_annotated(), r#"999("Hello")   / my-tag /"#);
Ok(())
}

In the next chapter we'll cover how to use tags in dCBOR.