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 Reducation: 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
, andnull
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, as the implemenation 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
Basic Usage
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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
Many common types are directly convertable 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.
Note that 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 simply 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
✅ NOTE: Observe the call to
clone()
above, which we need because thetry_from
method consumes theCBOR
value, and we still need an instance for the secondtry_from
call. Instances ofCBOR
are immutable, and thedcbor
library implments 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
The nice thing about this idiom is it's not just for numeric types. You can use it for any type that implements the TryFrom<CBOR>
, 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
Byte Strings
But 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 will convert 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 only allows 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 heterogenous 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 Vec
s 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 heterogenous array.
✅ NOTE: Of course, dCBOR doesn't support CBOR
undefined
or any of the other simple values, so thedcbor
API doesn't have ways to let you construct them!
Extracting from a Heterogenous 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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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 = i.to_cbor();
// 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)?;
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.clone())?;
assert_eq!(a as u8, b);
// Decode as an f64
let c = f64::try_from(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.clone())?;
assert_eq!(a, b);
// Cannot decode as a i32
assert!(u8::try_from(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.clone()).is_err());
// Decode as a String succeeds
let b = String::try_from(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.clone())?;
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)?.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_into_array()?;
// 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 = v2[0].clone().try_into_bool()?;
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, vec!("cat".into(), "dog".into(), "horse".into()));
h.insert(2, 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_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(())
}
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.
🚧 Work in Progress: More in this chapter and more chapters forthcoming!