Command line flags
--inputSpecifies the input CDDL file(s).
For a single file:
cddl-codegen --input examples/test.cddl --output export
If a directory is specified e.g. --input=some_dir then it will read all files in this directory (non-recursively).
The output format changes here. If there's a lib.cddl the types contained there are the standard output , and any other file e.g. foo.cddl will have its own module foo/mod.rs with its own foo/serialization.rs, etc.
cddl-codegen --input examples --output export
--outputSpecifies the output directory.
cddl-codegen --input examples --output export
--static-dirChanges the directory used for cddl-codegen's static runtime and template files. The default is static.
cddl-codegen --input=example --output=export --static-dir custom-static
--lib-nameSpecify the rust crate name for the output library. The wasm crate will have -wasm appended.
cddl-codegen --input=example --output=export --lib-name some-crate-name
--annotate-fieldsIncludes additional field-location context in generated deserialization errors. On by default; disabling it can slightly reduce generated code size.
Possible values: true, false
cddl-codegen --input=example --output=export --annotate-fields false
--to-from-bytes-methodsGenerates to_cbor_bytes() / from_cbor_bytes() methods on all WASM objects. On by default.
(The rust code doesn't need this as you can directly use the Serialize/Deserialize traits on them.)
Possible values: true, false
cddl-codegen --input=example --output=export --to-from-bytes-methods true
--wasmWhether to output a wasm crate. On by default.
Possible values: true, false
cddl-codegen --input=example --output=export --wasm false
--binary-wrappersGenerates named Rust wrapper types for byte string definitions instead of treating aliases to bytes/bstr as the primitive byte type. Off by default.
Possible values: true, false
cddl-codegen --input=example --output=export --binary-wrappers true
--no-synthesized-rust-collection-aliasesSuppresses emission of Rust pub type aliases for generator-synthesized collection wrappers — currently a table rule's auto-named keys-list, e.g. pub type FooList = Vec<Foo>; minted for tbl = { * foo => uint }. Off by default.
Rule-declared aliases are never suppressed, even when structurally transparent: an explicitly authored foo_list = [* foo] or signature = bytes .size 32 is a human-written name and always stays. The suppression is emission-only — generated code references collections structurally (Vec<Foo>), never through the alias, so no field type or serialization changes, and the crate still compiles. Wasm-side wrappers and aliases are untouched.
This is a flag rather than a default change because it removes public API (the crate's own synthesized aliases, not just extern-element ones) that existing consumers may depend on.
Possible values: true, false
cddl-codegen --input=example --output=export --no-synthesized-rust-collection-aliases true
--preserve-encodingsPreserves CBOR encoding upon deserialization e.g. definite vs indefinite, map ordering. For each module this will also create a cbor_encodings.rs file to potentially store any structs for storing these encodings. This option is useful if you need to preserve the deserialized format for round-tripping (e.g. hashes) or if you want to modify the format to coincide with a specific tool for hashing.
Possible values: true, false
cddl-codegen --input=example --output=export --preserve-encodings true
--canonical-formRequires --preserve-encodings (the canonical toggle is emitted on the preserve-encodings serialize signatures; on its own the generated crate does not compile, so the combination is rejected). Provides a way to override the specific deserialization format and to instead output canonical CBOR. This will have Serialize's trait have an extra to_canonical_cbor_bytes() method. Likewise the wasm wrappers (with --to-from-bytes-methods) will contain one too.
Canonical form follows RFC 7049 §3.9 "Canonical CBOR": definite, minimal-length encodings, and map keys sorted length-first, then bytewise. Note this key ordering differs from RFC 8949 §4.2.1 "deterministic encoding" (pure bytewise-lexicographic) whenever a longer-encoded key is bytewise smaller — e.g. the uint key 256 sorts after the text key "a" under RFC 7049 but before it under RFC 8949.
Possible values: true, false
cddl-codegen --input=example --output=export --preserve-encodings true --canonical-form true
--json-serde-derivesDerives serde::Serialize/serde::Deserialize for types to allow to/from JSON
Possible values: true, false
cddl-codegen --input=example --output=export --json-serde-derives true
--json-schema-exportTags types with sonSchema derives and generates a crate (in wasm/json-gen) to export them. This requires --json-serde-derives.
Possible values: true, false
Default: true
Example:
cddl-codegen --input=example --output=export --json-schema-export true
--package-jsonGenerates a npm package.json along with build scripts (some of these scripts require --json-serde-derives/--json-schema-export to work).
Possible values: true, false
Default: false
cddl-codegen --input=example --output=export --package-json true --json-schema-export true
--common-import-overrideOverrides the location of the static exports (e.g. error.rs, serialization.rs, etc).
This is particularly useful for combining multiple crates each generated using cddl-codegen where they all share a shared core directory where the static files are located.
Runtime-flavor contract: a crate generated without --preserve-encodings can target a preserve-flavored common crate (like cml_core). The generated deserializers construct CBORReadLen through From<cbor_event::Len>, which every crate exported by cddl-codegen (and cml_core) provides, so the cbor_event::Len a non-preserve crate reads is accepted by a preserve-flavored CBORReadLen. The mirror direction is not supported: a crate generated with --preserve-encodings cannot target a non-preserve-flavored common crate (there is no From<cbor_event::LenSz>).
Default: crate
cddl-codegen --input=example --output=export --common-import-override=cml_core
--export-static-dirAdditionally writes the composed rust static runtime — error.rs, the serialization.rs prelude (the static runtime only, no generated per-type impls), ordered_hash_map.rs, non_empty.rs, and non_empty_map.rs — into <dir> (created if needed), independent of whether in-crate static export happens.
This is the supported upgrade path for --common-import-override users. Override users own their copy of the static runtime in a shared crate, so cddl-codegen otherwise writes no static files for them — and that copy silently rots when the emitter and the runtime change together (e.g. a widening of the deserialization range-check type). Point this flag at the shared crate's runtime directory and regenerate: the freshly composed runtime is written, and the comment/code-preservation overlay carries any hand additions in those files forward (unless --no-preserve-comments).
Pure function of the flag set. Unlike the in-crate static export (which gates non_empty.rs / non_empty_map.rs on [+ …] / {+ … => …} spec usage, and raw_bytes_encoding on .cbor/.bytes usage), the exported directory is a pure function of the flags, never of the spec that happened to be run — a shared runtime crate serves many specs, so which spec was run must not change the output. Therefore non_empty.rs, non_empty_map.rs, and the raw_bytes_encoding prelude are always written. Flavor selection is otherwise identical to the in-crate composer: ordered_hash_map.rs appears only under --preserve-encodings; the serialization.rs prelude picks the preserve / canonical branches and appends the depth-guard runtime under --deserialize-depth-limit; the json / schemars companions append under --json-serde-derives / --json-schema-export; and non_empty_map.rs uses OrderedHashMap (not BTreeMap) under --preserve-encodings.
No mod.rs / lib.rs is written — the target crate owns its module declarations. The static files reference siblings via super::…, so a flat module directory works.
cddl-codegen --input=example --output=export --common-import-override=cml_core \
--export-static-dir=../cml-core/rust/src/generated
--extern-wasm-crateMaps a cross-crate extern dependency (declared under _CDDL_CODEGEN_EXTERN_DEPS_DIR_/<dep>/…) to the crate that holds its wasm-bindgen wrappers. Repeatable; each value is <dep>=<wasm_crate> (a malformed value, or a <dep> that names no extern dependency in the spec, aborts generation).
Use it when a dependency's wasm bindings live in a separate crate — the split <dep> / <dep>-wasm layout cddl-codegen itself generates (e.g. cml-core / cml-core-wasm). In the wasm pass, the use import and the wasm-boundary type path for that dep's types (as a direct member, and as elements of generated list/map wrappers) are qualified through the wasm crate, while the wrapper's inner storage stays the dep's rust type. Without a mapping the dep keeps its rust crate name for both passes — the single-crate convention, where the dependency's rust types are themselves #[wasm_bindgen]-annotated, which continues to work with no configuration.
The mapped wasm crate must mirror the rust crate's module tree (as cddl-codegen-generated wasm crates do: thin root + glob re-export), so dep::sub::Foo resolves to dep_wasm::sub::Foo. It must also provide, for each boundary type, From<native> for wrapper / From<wrapper> for native (the generated get/add/insert bodies convert with .clone().into()).
The generated wasm Cargo.toml then needs both dep crates as dependencies (the rust crate for inner storage, the wasm crate for the boundary). cddl-codegen does not add extern-dep entries to manifests — you add them by hand and the manifest merge preserves them.
cddl-codegen --input=example --output=export --common-import-override=cml_core --extern-wasm-crate=cml_core=cml_core_wasm
--extern-wrapper-indexPoints the consumer at a dependency's committed collection-wrapper index so it defers to the dependency's wasm wrappers instead of re-minting them. Repeatable; each value is <dep>=<path/to/collections.rs> (a malformed value, a <dep> that names no extern dependency, or an index file with a line that is not pub use …::<Name>; / blank / //-comment, aborts generation).
Every wasm-emitting run writes wasm/src/generated/collections.rs — a self-validating index of one pub use crate::…::<Wrapper>; line per collection-wrapper class the crate mints (see Output format). Point this flag at a dependency's copy of that file. For each wrapper the consumer would mint whose element / key / value types are all extern types of <dep>, if its structurally-derived name appears in <dep>'s index the consumer emits a plain use <dep_wasm>::collections::<Name>; (routed through --extern-wasm-crate, or the dep's rust crate name when unmapped) instead of a local class. Two crates each defining the same #[wasm_bindgen] wrapper would otherwise fail to link a single wasm cdylib with a duplicate symbol: __wbg_<wrapper>_free.
- An all-extern wrapper of one dependency whose name is absent from that dep's index is minted locally, with an stderr warning naming it (a dep-side inventory change that shifts ownership back to the consumer is then loud in the regen log).
- A mixed-element wrapper (a consumer type, or types from more than one dependency) is always minted locally and silently — it can only live in the consumer.
The consumer's own collections.rs lists only the wrappers it mints itself; deferred wrappers are brought in by a plain private use (never re-exported), so they never enter the consumer's public API and each crate's index is a precise inventory of what it owns — which is exactly what its consumers point --extern-wrapper-index at.
Regeneration order: the dependency must be (re)generated before the consumer — the index is committed generated output and part of the dependency's cross-crate interface, so it must reflect the dependency's current wrapper inventory when the consumer reads it. This matches the existing multi-crate regeneration pattern (a driver script regenerating crates in dependency order).
Typically paired with --extern-wasm-crate (which supplies the <dep_wasm> crate name the deferred use is qualified through). All flags default off; output with no new flags is byte-identical to before.
cddl-codegen --input=example --output=export --common-import-override=cml_core \
--extern-wasm-crate=cml_core=cml_core_wasm \
--extern-wrapper-index=cml_core=../cml-core/wasm/src/generated/collections.rs
--workspace-depMarks an extern dependency as a co-generated workspace member. For every collection wrapper whose element types are all owned (transitively — nested lists/maps resolve to their named leaves) by that single dependency, the consumer defers unconditionally — it emits use <dep_wasm>::collections::<Name>; at use sites (routed through --extern-wasm-crate) and never mints a local #[wasm_bindgen] class, regardless of any --extern-wrapper-index. This closes the sibling-collision class: two consumers each minting the same FooList would otherwise define one JS class twice and fail to link a single wasm cdylib. Repeatable; each value is a bare <dep> name (the <dep>=<host> host form for unmodifiable external deps is reserved but not yet supported). Each named dependency must be a configured extern dependency and have an --extern-wasm-crate mapping.
The consumer additionally emits wasm/src/generated/borrowed_collections.rs whenever the flag is present (empty-but-present when nothing is borrowed — stable presence, stable diffs): the mirror of collections.rs ("what I provide" ↔ "what I borrow, from whom"). It carries a private #[allow(unused_imports)] mod borrowed of use lines (the compile-checked half — a wrapper a dependency stops providing fails this crate's build, naming the type) and a BORROWED_SHAPES table whose first column is the dependency's rust crate name (see Output format).
The consumer also emits rust/src/generated/borrowed_key_types.rs (same presence semantics), the rust-crate analog for map-key derives. A consumer map keyed on a dependency's type needs that type to derive Eq/Ord/PartialOrd (plus Hash under --preserve-encodings), but the derive lives in the dependency's crate — and when the map mixes a dependency key with a consumer-owned value ({* dep_key => my_local}) the wrapper is not all-one-dep, so it never enters borrowed_collections.rs, yet the dependency must still key-derive dep_key. This file records every such borrowed key type: a _borrowed_key_types_self_check fn (the compiled half — a dependency dropping a derive fails this crate's build, naming the type) and a BORROWED_KEY_TYPES table of (dep rust-crate name, cddl ident) rows the dependency re-reads via --key-requests. When a borrowed key carries a @used_as_key flavor (hash/ord) rather than the bare full bundle, rows gain a third flavor column and the self-check splits into per-flavor bound carriers, so a hash-only borrow is never checked against Ord — see Output format for the format details and the version seam.
- Ownerless wrappers (no named element types, e.g.
{* uint => text}) and mixed-dep wrappers are never workspace-borrowed — they keep the shipped--extern-wrapper-indexdeferral / local-mint behavior. Passing both flags for the same dependency is legal: workspace-borrow the owned shapes, index-defer the ownerless ones. - A consumer's own rule-declared type whose name equals a wrapper it would otherwise borrow is never suppressed; it mints locally and generation warns that it will duplicate-symbol against the dependency's class (remedy: rename the rule or give it a distinct
@name).
The sidecar it emits, the dependency-hosted requested_collections.rs, and the reverse-dependency-order regeneration contract (holistic regen = one pass, zero diff when unchanged; a consumer-alone regen that adds a borrow fails loudly until the dependency regens; removals are a benign superset) are documented under Output format § Workspace mode.
Requires --extern-wasm-crate. Defaults off; output with no new flags is byte-identical to before.
cddl-codegen --input=example --output=export --common-import-override=cml_core \
--extern-wasm-crate=cml_core=cml_core_wasm \
--workspace-dep=cml_core
Shapes this flag does not cover (the manual override). --workspace-dep automates only all-one-dep wrappers that generated consumer code borrows. For a mixed-dep wrapper (elements from more than one dependency) or a wrapper needed by hand-written consumer code that no generated code requests, the zero-tool-change interim remains the answer and stays useful permanently: declare the wrapper as a rule in the owning dependency's spec, named to match the structural name (stake_credential_list = [* stake_credential] → StakeCredentialList; for a mixed-dep shape, declare it in the latest involved dependency's spec — the general placement rule applied by hand). Rule-declared wrappers land in that crate's collections.rs index by construction, so every consumer's --extern-wrapper-index deferral picks them up. The not-in-index stderr warning prints the exact paste-able rule line — <snake_name> = <shape> ; requested by <consumer> — telling you precisely which rule to add and where.
Degenerate case: the shared host crate (documented, not yet implemented). For an extern dependency that cannot be modified (external, separately released), the same mechanism degenerates to a designated host — a workspace member with an empty or minimal spec whose sole purpose is to own wrappers over the external dependency's types. Consumers would point the placement at the host, their sidecar entries would name it, and the host's regen would consume --wrapper-requests exactly as any dependency does (regenerated last, since every consumer depends on it). The <dep>=<host> value form for --workspace-dep is reserved for this; the host defaults to the dependency itself. Not yet implemented — until then, the manual override (declare the wrapper as a rule in a commonly-depended workspace crate's spec and index-defer) covers the case.
--wrapper-requestsThe dependency-side companion to --workspace-dep: one <consumer>=<path> per consumer, each pointing at that consumer's committed wasm/src/generated/borrowed_collections.rs. The dependency parses each sidecar strictly (any content outside the frozen format — a compile_error!/unpreserved-comment trap, an unknown line, a mangled row — is a hard error naming the file), takes the entries addressed to itself (dep column == this crate's normalized --lib-name), unions the requested collection-wrapper shapes across consumers, and emits every requested wrapper it does not already produce into wasm/src/generated/requested_collections.rs — indexed in its own collections.rs, each carrying a /// Generated at the request of: <consumers, sorted>. doc. This hosts the wrapper in the dependency so sibling consumers import one definition instead of each minting a colliding #[wasm_bindgen] class.
- The union is keyed by the wrapper's shape, not its name; the derived structural name is cross-checked against the listed name. Hard errors (each with an actionable message): a shape referencing an element the dependency doesn't own; a directly wasm-exposable shape (it lowers to a bare
Vec<…>with no wrapper class — the stub-fidelity diagnosis below); a name↔shape mismatch (the message names each leaf element and how the dependency resolves it); a shape the dependency already produces under a non-structural rule name (remedy: rename the rule,@nameit, or drop it); two requested shapes deriving the same structural name; a nested shape whose inner wrapper is neither requested nor own-produced; a malformed sidecar (including a reserved CDDL/rust identifier as an element, or pathological shape nesting). - A requested shape the dependency already produces under its structural name is satisfied by the existing class — nothing is emitted for it.
- Output is byte-identical regardless of the order of the
--wrapper-requestsflags (everything is sorted before emission). Repeatable;<consumer>is a label used only in attribution and error messages.
The stub-fidelity contract. Consumer and dependency derive wrapper names independently — the consumer from its _CDDL_CODEGEN_EXTERN_DEPS_DIR_ stub, the dependency from its real spec — so the two agree only when the stub is representation-faithful: declare _CDDL_CODEGEN_EXTERN_TYPE_ only for types the dependency actually exports as wrapper classes, and spell transparent types truthfully — a primitive alias as its definition (coin = uint, transaction_index = uint .size 2), a c-style value enum as its choices (fe = 0 / 1 / 2), and never stub an @no_alias rule as an opaque extern. An unfaithful stub makes the consumer borrow a wrapper the dependency has no class for; the dependency's regen then hard-errors with a stub-fidelity diagnosis naming the element, how the dependency resolves it, and the stub fix (after which the regenerated consumer stops borrowing that shape). One operational red flag: [+ …]/{+ …} NonEmpty shapes always get wrapper classes, so a NonEmpty request hosting cleanly while its loose [* …] twin errors is the signature of a stubbed-opaque transparent element.
With no --wrapper-requests flag the output is byte-identical to before (the file is not emitted).
The requested_collections.rs file format (attribution docs, index re-export, NonEmpty… support wrappers) and the reverse-dependency-order regeneration contract this flag participates in are documented under Output format § Workspace mode.
cddl-codegen --input=cml_core.cddl --output=export --lib-name=cml-core --wasm=true \
--wrapper-requests=cml_chain=../cml_chain/wasm/src/generated/borrowed_collections.rs \
--wrapper-requests=cml_multiera=../cml_multiera/wasm/src/generated/borrowed_collections.rs
--key-requestsThe dependency-side companion to a consumer's rust/src/generated/borrowed_key_types.rs (the map-key-derive channel — see --workspace-dep above): one <consumer>=<path> per consumer. The dependency parses each sidecar strictly (any content outside the frozen format — a compile_error!/unpreserved-comment trap, an unknown line, a mangled row — is a hard error naming the file), takes the rows addressed to itself (dep column == this crate's normalized --lib-name), resolves each borrowed CDDL ident to a type in its own spec, and marks it used-as-key before finalize computes the key-derive set. The dependency then derives the requested traits on that type and — since finalize expands the seed transitively through the type's private fields — on everything it recursively contains. A two-column (bare) row requests the full bundle: Eq/Ord/PartialOrd (plus Hash under --preserve-encodings); a row carrying a @used_as_key flavor column (hash/ord) requests exactly that family, so e.g. a hash-only key never forces Ord through types that cannot supply it.
This covers the case the all-one-dep sidecar structurally can't: a consumer map mixing this dependency's key with a consumer-owned value ({* dep_key => my_local}) never enters borrowed_collections.rs, so only --key-requests conveys that dep_key must be key-capable. (An all-one-dep struct-keyed map {* dep_key => uint} does record its wrapper in borrowed_collections.rs, and the dependency also seeds that key from the --wrapper-requests shape — so passing both flags is the norm.)
- A row naming a type the dependency no longer defines is a hard error naming the consumer and file (a consumer keying on a deleted type must be loud, mirroring the sidecar's compiled self-check). Rows addressed to other dependencies are ignored.
- Output is byte-identical regardless of the order of the
--key-requestsflags (seeds are collected into a sorted set). Repeatable;<consumer>is a label used only in error messages. With no--key-requestsflag the output is byte-identical to before.
cddl-codegen --input=cml_core.cddl --output=export --lib-name=cml-core --wasm=true --preserve-encodings=true \
--key-requests=cml_chain=../cml_chain/rust/src/generated/borrowed_key_types.rs \
--key-requests=cml_multiera=../cml_multiera/rust/src/generated/borrowed_key_types.rs
--wasm-cbor-json-api-macroIf it is passed in, it will call the supplied externally defined macro on each exported type, instead of manually exporting the functions for to/from CBOR bytes + to/from JSON API.
The external macro is assumed to exist at the specified path and will be imported if there are module prefixes.
The macro must take the wasm wrapper type as the only parameter.
This macro will be called regardless of the values of to-from-bytes-methods / json-serde-derives / etc, so it is assumed that whatever logic your macros have is consistent with the other CLI flag values.
If the macro's expansion names a collection type (BTreeMap, OrderedHashMap, NonEmptyVec, NonEmptyMap), it must fully-qualify it ($crate::... / std::collections::BTreeMap) rather than rely on the call-site file's ambient imports — imports are emitted only for names the generated code itself references, and ident-scanning cannot see through macro expansion (the in-repo stub macro crate already complies and is compile-gated).
cddl-codegen --input=example --output=export --wasm-cbor-json-api-macro=cml_core_wasm::impl_wasm_cbor_json_api
--wasm-conversions-macroIf it is passed in, it will call the supplied externally defined macro on each exported type, instead of manually exporting the rust/wasm conversion traits.
The external macro is assumed to exist at the specified path and will be imported if there are module prefixes.
The macro must take the rust type as the first parameter and the wasm wrapper type as the second one.
If the macro's expansion names a collection type (BTreeMap, OrderedHashMap, NonEmptyVec, NonEmptyMap), it must fully-qualify it — never rely on the call-site file's ambient imports (see --wasm-cbor-json-api-macro above; ident-scanning cannot see through macro expansion).
cddl-codegen --input=example --output=export --wasm-conversions-macro=cml_core_wasm::impl_wasm_conversions
--wasm-list-macroIf it is passed in, it will call the supplied externally defined macro on each generated WASM list wrapper (Vec<T>-backed), instead of emitting the inline struct + accessor block (new/len/get/add) + conversion traits. Map wrappers are unaffected.
(The list wrapper types themselves exist for ownership safety at the wasm boundary — see Wasm Differences § Heterogeneous Arrays; this flag only changes how their boilerplate is emitted.)
The external macro is assumed to exist at the specified path and will be imported if there are module prefixes.
The macro must take five parameters:
- the rust element type
- the wasm element type
- the wasm wrapper name
- a
needs_intobool (whether the element crosses the wasm boundary via.into()) - an
is_copybool. e.g.impl_wasm_list!(cml_lib::Foo, Foo, FooList, true, false);
The first two parameters can be generic types (e.g. Vec<u8>, BTreeMap<u64, String>), so match them with :ty fragments (not :path/:ident); the third is an identifier (:ident).
This supersedes --wasm-conversions-macro for list wrappers (the list macro is expected to emit the conversion traits itself); non-list types are unaffected. A list whose element type can't be reduced to those two bools (e.g. an optional element) falls back to the inline form.
The restricted non-empty list wrappers ([+ T] → NonEmptyBarList; see Wasm Differences § Non-empty containers) always emit inline in this phase — only the loose builder wrapper takes the macro path. The restricted wrapper's boundary surface (the failable try_from door, new(first), checked removal) differs from the plain new/len/get/add block the macro emits, so a parallel macro for it is deferred; there is no flag needed to opt into the inline form.
If the macro's expansion names a collection type (BTreeMap, OrderedHashMap, NonEmptyVec, NonEmptyMap), it must fully-qualify it — never rely on the call-site file's ambient imports (see --wasm-cbor-json-api-macro above; ident-scanning cannot see through macro expansion). Types passed as macro arguments are unaffected — call-site tokens are scanned.
(The no-argument form of this flag is reserved for a future built-in default that would also emit the macro definition; for now the macro definition must be supplied by you, e.g. cml_core_wasm::impl_wasm_list.)
cddl-codegen --input=example --output=export --wasm-list-macro=cml_core_wasm::impl_wasm_list
--emit-testsEmits a #[cfg(test)] mod cddl_generated_tests into the generated rust crate, with two kinds of tests derived from each type's structure:
- Round-trip tests (
roundtrip_<type>): for every constructible type, value cases derived from the type itself — a valid baseline, bound boundaries, one case per choice variant, and each optional field additionally present — each asserted byte-identical through the full wire cycle (value → to_cbor_bytes → from_cbor_bytes → to_cbor_bytes). - Reject tests (
reject_<type>): for every type with a bounded (RangeCheck) field, boundary values are accepted and out-of-bounds values rejected — on deserialization for structs (mutate apubfield out of bounds, serialize, confirmfrom_cbor_bytesrejects it), and at the constructor for type/group choices and bounded@newtypewrappers.
Requires --to-from-bytes-methods. Values are minted deterministically from the type structure (no proptest/Arbitrary dependency is added to the generated crate). Shapes that can't be cheaply constructed — bounded nint fields, transparent table/array aliases at top level, types referencing user-supplied code — are skipped with a logged notice, never silently.
With directory (multi-file) input, the test module still lands at the generated root and covers the types of every module: it glob-imports each generated submodule (use super::<module>::*;), so cross-module values (a type in one module holding a type from another) are constructed and round-tripped like root-scope ones. Two submodules exporting the same type name would make those glob imports ambiguous (rustc E0659) — avoided by not reusing a rule name across modules.
When --preserve-encodings is also set, each round-trip case additionally runs an encoding-fidelity check: a small self-contained CBOR mutator (emitted alongside the tests) derives irregular re-encodings of the minted value's canonical bytes — non-minimal header widths, indefinite array/map framing, chunked strings, reversed map key order, and all of these composed — and asserts every variant decodes and re-encodes byte-identically (the --preserve-encodings contract). With --canonical-form also set, each variant is additionally asserted to canonicalize to the same bytes (encoding-invariance) plus a per-case canonical fixed point. Types whose wire format is partly user-supplied (@custom_serialize/@custom_deserialize, reachable through the type's fields) are excluded from this check, since their accept/re-emit behavior is not the generated serializer's. All variant classes run for every other type, including variable-length arrays/maps of CBOR major-type-7 elements or keys (bool, nil, float, or an optional position): the generated indefinite-length break-check probes for the 0xff break with the non-consuming special_break(), so such an element/key is read normally rather than mistaken for the break.
When --wasm=true is also set, a matching #[cfg(test)] mod cddl_generated_wasm_tests is emitted into the generated wasm crate. It renders each minted value twice — through the wasm wrapper API and through the cddl_lib:: rust API the wasm crate path-depends on — and asserts a cross-crate byte differential (both to_cbor_bytes() byte-equal, catching a wrong wasm-boundary conversion the rust half can't see), a wire round-trip, accessor read-back against the minted literals, and boundary acceptance. It shares the same --to-from-bytes-methods requirement (the wasm CBOR methods it drives), and is additionally skipped when a --wasm-*-macro flag is set (those macros replace the per-type wrapper method surface it targets). Shapes with no faithful wasm build — wrapper/collection ctor args, @newtype/tag wrappers, flatten points — are loud-skipped the same way. cargo check never compiles #[cfg(test)] code, so the emitted module only runs under a cargo test of the wasm crate.
Possible values: true, false
Default: false
cddl-codegen --input=example --output=export --emit-tests true
--emit-tests-conformanceAdds an independent conformance oracle to every --emit-tests round-trip case. Right after a case computes its CBOR bytes, a cddl_conformance::validate(&bytes, "<rule>") call validates them against the source .cddl rule using the cddl crate's validator — a decode + constraint-evaluation path independent of the generated encoder/decoder.
Because the round-trip harness mints its values from the same IR as the code under test, an IR-level miscompile (a bound or member computed wrong at parse time) mints a spec-violating value and then asserts it round-trips green. This oracle closes that gap: the validator rejects the out-of-spec bytes even though the round-trip agrees with itself.
Requires --emit-tests. The generated test crate then needs the cddl dependency and the source spec on disk next to its Cargo.toml as cddl_conformance_source.cddl (the shared oracle helpers from tests/deser_test_conformance.rs — cddl_oracle_load_spec / assert_cddl_conforms — must also be in scope). It shares the fork's parser with the generator, so it catches wrong values, not fork-level misparses, and the validator has known gaps (e.g. it hard-errors on .size over a signed int instead of applying the per-value semantics the RFC author clarified in cbor-wg/cddl#32 — match non-negative values in the uint .size window, non-match negatives; the fork's pinned rev enforces uint control ops and non-uint-endpoint ranges, which released cddl 0.10.x does not). Intended for the maintainer's manual/local IR-conformance sweep, not everyday generation.
Possible values: true, false
Default: false
cddl-codegen --input=example --output=export --emit-tests true --emit-tests-conformance true
--deserialize-depth-limitOpt-in recursion depth guard for the generated deserializers. Generated composite deserializers are recursive-descent, so a recursive type (e.g. tree = [value: uint, children: [* tree]], Plutus-style data) has no intrinsic depth bound: maliciously deep CBOR recurses until the thread's stack overflows and the process aborts (SIGABRT — uncatchable, not a returnable error).
When set to N, every generated composite deserialize acquires an RAII depth guard at entry (a thread-local counter, decremented on every return path including ?-propagation); once nesting exceeds N, deserialization returns a graceful DeserializeError (DeserializeFailure::DepthLimitExceeded) instead of recursing further. The limit is baked into the generated crate at generation time.
This rejects any document nested deeper than N, including spec-valid ones — that is the point of it being opt-in. cddl-codegen must not invent a data limit the spec doesn't have, so there is no default guard: leaving the flag off produces output byte-identical to a build without it. Enable it when your consumer deserializes untrusted input (e.g. on-chain data), where the unbounded default's stack-overflow abort is a DoS. Choose N above the deepest nesting any legitimate document reaches.
Possible values: any unsigned integer (unset = off)
Default: off (no guard)
cddl-codegen --input=example --output=export --deserialize-depth-limit 128
--no-preserve-commentsDisables the edit-preservation overlay — user comments AND tagged code blocks alike.
By default, when a generated src/generated/** .rs file already exists on disk, the hand edits it carries are re-applied onto the freshly generated content: own-line comments are re-anchored by symbol identity and token equality (never a textual diff), // cddl-codegen:insert-start/insert-end blocks re-insert your added code, and // cddl-codegen:replace-start/replaces/replace-end blocks re-apply your code swaps by matching their recorded original in the fresh output. Anything that cannot be safely re-placed is not dropped silently — it is trapped in a tagged compile_error! block that fails the generated crate's build with your original text in the message, and carries forward until you delete it. Outside your tagged regions the overlay never touches a generated code token, and running the tool twice equals running it once.
See preserving edits for the block syntax, the anchoring rules, every failure message with its resolution, and the residual limits (trailing comments, tool-owned doc text, unlexable files, orphaned files).
Pass --no-preserve-comments to skip the overlay entirely and clobber each generated file with pristine output. With the overlay off, tagged blocks in the prior output are clobbered like any other edit.
Possible values: the flag takes no value; its presence disables preservation
Default: preservation on
cddl-codegen --input=example --output=export --no-preserve-comments