This file is the short contract for code agents generating or migrating
cbor2 integrations. Prefer these rules over guessing from generic serde or
CBOR examples.
The default build (cbor2 = "1") is std and already covers to_vec,
to_writer, from_slice, from_reader, validate, Value, Simple,
RawValue, cbor!, the canonical encoders, diagnostics and the synchronous
async_io helpers. Add a feature only for the rows that need one:
| Need | Manifest line |
|---|---|
#[derive(cbor2::Cbor)] |
cbor2 = { version = "1", features = ["derive"] } |
| tokio async adapters | cbor2 = { version = "1", features = ["tokio"] } |
| futures async adapters | cbor2 = { version = "1", features = ["futures"] } |
no_std + heap |
cbor2 = { version = "1", default-features = false, features = ["alloc"] } |
no_std, no heap |
cbor2 = { version = "1", default-features = false } |
Plain serde structs (#[derive(Serialize, Deserialize)], used by most recipes
here) also need serde = { version = "1", features = ["derive"] } in the
manifest. Generating code that uses a feature-gated API without enabling the
feature is the most common compile failure — set the manifest first.
| Task | Use | Do not use |
|---|---|---|
| Encode a serde value to memory | cbor2::to_vec |
Manual Value construction unless the shape is dynamic |
| Encode a serde value to a writer | cbor2::to_writer |
Building a Vec first when streaming is enough |
| Decode from an in-memory buffer | cbor2::from_slice |
from_reader if borrowed fields are expected |
Decode from Read |
cbor2::from_reader |
Borrowed output types |
| Require exactly one item in a buffer | cbor2::validate before/after decode |
Assuming from_slice rejects trailing bytes |
| Decode a CBOR sequence | cbor2::de::Deserializer::into_iter |
Repeated from_slice on the same buffer |
| Preserve exact encoded bytes | cbor2::RawValue |
Decode/re-encode through typed structs |
| Dynamic or unknown shape | cbor2::Value or cbor2::cbor! |
Untyped maps of JSON strings |
| Preserve CBOR simple values | cbor2::Simple or Value::Simple |
Collapsing registered simple values into ad hoc integers |
| Deterministic bytes for signatures | to_canonical_vec / to_canonical_writer |
Plain to_vec on maps with unspecified order |
| COSE integer keys, arrays, or tags | #[derive(cbor2::Cbor)] (feature derive) |
serde(rename = "1") for integer keys |
| Async read/write of a typed value | cbor2::async_io::{read_value, write_value} |
Treating serde itself as async |
| Async read from an untrusted stream | cbor2::async_io::read_value_with_limit / read_item_with_limit |
Unbounded helpers without an outer message limit |
| Async item when you must borrow or inspect raw bytes | cbor2::async_io::read_item then from_slice |
read_value when the buffer must outlive the call |
The cbor2-cli crate installs a cbor binary. Prefer text-safe commands when
you are an AI/code agent working in a terminal transcript:
| Task | Command |
|---|---|
| Inspect pasted CBOR hex/base64 or a file | cbor <INPUT> |
| Convert CBOR to JSON for jq-like tools | cbor decode --json <INPUT> |
| Preserve wire details while pretty-printing | cbor decode <INPUT> or bare cbor <INPUT> |
| Convert JSON to copyable CBOR bytes | echo '{"a":1}' | cbor encode --json --hex |
| Convert CDN to copyable CBOR bytes | printf "{1: h'dead'}" | cbor encode --cdn --hex |
| Pipe raw CBOR bytes to another binary command | cbor encode |
| Check one or more complete CBOR items | cbor validate <INPUT> |
For agent-generated examples, prefer cbor encode --hex over raw
cbor encode; raw binary stdout is hard to quote, diff and paste reliably.
Use --json when the input is intended to be strict JSON, or --diag/--cdn
when it is intended to be CDN.
from_sliceandfrom_readerdeserialize one leading CBOR item. They are not exact-buffer validators. Usevalidatewhen trailing data must fail.from_sliceis the borrowed path. Definite-length text and byte strings can deserialize as borrowed&strandserde_bytesvalues from the input.from_readercopies because it cannot borrow from a generic stream.- Indefinite-length text and byte strings can decode into owned targets, but cannot be borrowed as one contiguous slice.
- Plain
Vec<u8>and&[u8]are serde sequences and encode as CBOR arrays. Useserde_bytes::ByteBuf,serde_bytes::Bytes, or#[serde(with = "serde_bytes")]for CBOR byte strings. #[derive(cbor2::Cbor)]generates serdeSerializeandDeserialize. Do not also derive serde'sSerializeorDeserializeon the same type.#[cbor(key = 1)]creates an integer map key.#[serde(rename = "1")]creates the text key"1".#[cbor(array)]is for named structs whose CBOR wire shape is a field-order array. Do not combine it with per-field#[cbor(key = ...)].#[cbor(tag = N)]wraps the container in CBOR tagNon encode and treats tag layers as transparent on decode, so the same type accepts tagged or untagged input — no separate "bare" struct plus aFromimpl.- Generic CBOR simple values are represented by
cbor2::SimpleandValue::Simple. UseSimple::new(N)when a registered simple value such as SD-CWTsimple(59)must appear as a map key. async_io::read_value/write_valueframe and (de)serialize one CBOR item in a single call. Drop toread_item/write_itemonly to borrow from or inspect the raw item bytes; serde itself stays synchronous on the buffered item.- For untrusted async peers, prefer
read_value_with_limitorread_item_with_limitunless an outer transport layer already enforces a message size limit. - The bare
async_iotraits have no impls of their own. Enabletokioorfuturesand callasync_io::tokio::*/async_io::futures::*to drive realtokio::io/futures_iostreams.
| Existing code | cbor2 replacement |
|---|---|
serde_cbor::to_vec(value) |
cbor2::to_vec(value) |
serde_cbor::from_slice(bytes) |
cbor2::from_slice(bytes) |
ciborium::ser::into_writer(value, writer) |
cbor2::to_writer(value, writer) |
ciborium::de::from_reader(reader) |
cbor2::from_reader(reader) |
ciborium/serde_cbor dynamic values |
cbor2::Value plus Value::serialized / Value::deserialized |
| Signature payload decoded then re-encoded | cbor2::RawValue |
When migrating durable data, check byte-string-sensitive types explicitly.
Some serde visitors distinguish visit_bytes from visit_byte_buf; use
serde_bytes, a compatibility helper, or a cbor2::Value bridge when a type
expects CBOR byte strings.
See docs/agent-cookbook.md for copyable recipes and common mistakes.
Run cargo run --example agent_patterns for a compact executable tour of the
rules above.
For changes that affect public API, docs, or examples, use the relevant subset of:
cargo fmt --all --check
cargo test --workspace --all-targets --all-features
cargo test -p cbor2 --features derive
cargo test -p cbor2 --no-default-features --lib
cargo test -p cbor2 --no-default-features --features alloc --lib
cargo clippy --workspace --all-targets --all-features -- -D warnings
git diff --check