Skip to main content

Crate cbor_core

Crate cbor_core 

Source
Expand description

Deterministic CBOR encoder and decoder following the CBOR::Core profile (draft-rundgren-cbor-core-25).

The central type is an owned Value. It can be constructed, inspected, modified in place, encoded to bytes, and decoded back. The API follows CBOR’s own shape, so tagged values, simple values, and arbitrary map keys stay directly reachable without a detour through a schema.

§Types

Value is the owned representation of any CBOR data item. It handles construction, inspection, encoding, and decoding, and is what most code works with directly.

§Quick start

use cbor_core::{Value, array, map};

// Build a value
let value = map! {
    1 => "hello",
    2 => array![10, 20, 30],
};

// Encode to bytes and decode back
let bytes = value.encode();
let decoded = Value::decode(&bytes).unwrap();
assert_eq!(value, decoded);

// Access inner data
let greeting = decoded[1].as_str().unwrap();
assert_eq!(greeting, "hello");

// Round-trip through diagnostic notation
let text = format!("{value:?}");
let parsed: Value = text.parse().unwrap();
assert_eq!(value, parsed);

§Diagnostic notation

Value implements FromStr, so any CBOR value can be written as text and parsed with str::parse. This is often the shortest way to build a literal value in a test, a fixture, or an example, and it avoids manual Value::from chains for nested data.

The grammar is Section 2.3.6 of the CBOR::Core draft. Examples:

use cbor_core::Value;

// Integers in any base, with `_` as a digit separator
let v: Value = "0xff_ff_00_00".parse().unwrap();
assert_eq!(v, Value::from(0xff_ff_00_00_u32));

// Arbitrary precision: parsed as tag 2 / tag 3 big integers
let big: Value = "18446744073709551616".parse().unwrap();
assert_eq!(big, Value::from(u64::MAX as u128 + 1));

// Floats, including explicit bit patterns for NaN payloads
let f: Value = "1.5e2".parse().unwrap();
assert_eq!(f, Value::from(150.0));
let nan: Value = "float'7f800001'".parse().unwrap();
assert_eq!(nan.encode(), vec![0xfa, 0x7f, 0x80, 0x00, 0x01]);

// Byte strings: hex, base64, ASCII, or embedded CBOR
assert_eq!("h'48656c6c6f'".parse::<Value>().unwrap(), Value::from(b"Hello".to_vec()));
assert_eq!("b64'SGVsbG8'".parse::<Value>().unwrap(), Value::from(b"Hello".to_vec()));
assert_eq!("'Hello'".parse::<Value>().unwrap(), Value::from(b"Hello".to_vec()));
// << ... >> wraps a CBOR sequence into a byte string
assert_eq!(
    "<< 1, 2, 3 >>".parse::<Value>().unwrap(),
    Value::ByteString(vec![0x01, 0x02, 0x03]),
);

Nested structures are written directly, and maps may appear in any order. The parser sorts keys and rejects duplicates:

use cbor_core::Value;

let cert: Value = r#"{
    / CWT-style claims, written out of canonical order /
    "iss": "https://issuer.example",
    "sub": "user-42",
    "iat": 1700000000,
    "cnf": {
        "kty": "OKP",
        "crv": "Ed25519",
        "x":   h'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a'
    },
    "scope": ["read", "write"]
}"#.parse().unwrap();

assert_eq!(cert["sub"].as_str().unwrap(), "user-42");
assert_eq!(cert["cnf"]["crv"].as_str().unwrap(), "Ed25519");

Supported grammar elements: integers (decimal, 0x, 0o, 0b, with _ separators), arbitrary-precision integers, floats (decimal, scientific, NaN, Infinity, float'<hex>'), text strings with JSON-style escapes and surrogate pairs, byte strings (h'...', b64'...', '...', <<...>>), arrays, maps, tagged values N(...), simple(N), true, false, null, single-line # ... comments, and block / ... / comments.

The parser accepts non-canonical input (for example unsorted maps and non-shortest numbers), normalizes it, and produces a canonical Value. Round-tripping format!("{v:?}").parse::<Value>() always yields the original value.

§Encoding rules

Encoding is deterministic: integers and floats use their shortest form, and map keys are sorted in canonical order. The decoder rejects input that deviates.

NaN payloads, including signaling NaNs, survive round-trips bit-for-bit. Float-width conversions go through bit patterns to avoid hardware canonicalization.

§Sequences

A CBOR sequence (RFC 8742) is zero or more items concatenated without framing. The library reads and writes sequences in all three formats selected by Format.

On the read side, DecodeOptions::sequence_decoder wraps a byte slice and yields a SequenceDecoder with Item = Result<Value, Error>. DecodeOptions::sequence_reader wraps any io::Read and yields a SequenceReader with Item = Result<Value, IoError>.

In binary and hex, items sit back-to-back. In diagnostic notation, items are comma-separated, with an optional trailing comma.

On the write side, SequenceWriter::new takes an io::Write and a Format, to select binary, hex, or diagnostic output. Three methods feed items in:

MethodInput
write_item&Value
write_itemsIntoIterator<Item = &Value>
write_pairsIntoIterator<Item = (&Value, &Value)>

write_pairs emits each key and value as two consecutive items, matching the shape of &BTreeMap::iter(), so a map held in a Value streams straight into a sequence.

Array and Map bridge between a sequence and an owned collection:

ConstructorInputBehavior
Array::from_sequenceIntoIterator<Item = Value>collects into an array
Array::try_from_sequenceIntoIterator<Item = Result<Value, E>>short-circuits on the first error
Map::from_pairsiterator of (Value, Value)last write wins on duplicate keys
Map::try_from_pairsiterator of (Value, Value)rejects duplicates with Error::NonDeterministic
Map::from_sequenceIntoIterator<Item = Value>alternating key/value; strict canonical order
Map::try_from_sequenceIntoIterator<Item = Result<Value, E>>fallible-input form of from_sequence

The try_* forms take fallible iterators directly, so a SequenceDecoder or SequenceReader can feed an Array or Map without an intermediate Vec. Map::try_from_sequence uses the bound E: From<Error>, which covers both iterators because IoError already has From<Error>.

use cbor_core::{Array, DecodeOptions, Format, SequenceWriter, Value};

let items = [Value::from(1), Value::from("hi"), Value::from(true)];

let mut buf = Vec::new();
SequenceWriter::new(&mut buf, Format::Binary)
    .write_items(items.iter())
    .unwrap();

let array = Array::try_from_sequence(
    DecodeOptions::new().sequence_decoder(&buf),
).unwrap();
assert_eq!(array.get_ref().as_slice(), &items);

§Optional features

FeatureAdds
serdeSerialize/Deserialize for Value, serde::to_value, serde::from_value
chronoConversions between chrono::DateTime and DateTime/EpochTime/Value
timeConversions between time::UtcDateTime/OffsetDateTime and DateTime/EpochTime/Value
jiffConversions between jiff::Timestamp/Zoned and DateTime/EpochTime/Value
halfFrom/TryFrom between Float/Value and half::f16
num-bigintFrom/TryFrom between Value and num_bigint::BigInt/BigUint
crypto-bigintFrom/TryFrom between Value and crypto_bigint::Uint/Int/NonZero
rugFrom/TryFrom between Value and rug::Integer

Modules§

serdeserde
Serde integration for CBOR Value.

Macros§

array
Construct a CBOR array from a list of expressions.
map
Construct a CBOR map from a list of key => value pairs.

Structs§

Array
Conversion helper for Value::array.
DateTime
Helper for validated date/time string construction.
DecodeOptions
Configuration for CBOR decoding.
EpochTime
Helper for validated epoch time construction.
Float
A floating-point value stored in its shortest CBOR encoding form.
Map
Conversion helper for Value::map.
SequenceDecoder
Iterator over a CBOR sequence stored in a byte slice.
SequenceReader
Iterator over a CBOR sequence pulled from an io::Read source.
SequenceWriter
Streaming writer for CBOR sequences in binary, hex, or diagnostic notation.
SimpleValue
A CBOR simple value (major type 7, values 0-23 and 32-255).
ValueKey
A key for looking up elements in Value arrays and maps.

Enums§

DataType
Kind of a Value.
Error
Errors produced by this crate.
Format
Format for CBOR decoding or encoding.
IoError
Error type for IO related operations.
Value
A single CBOR data item.

Type Aliases§

IoResult
Convenience alias for streaming CBOR operations.
Result
Convenience alias used throughout this crate.