CBOR decoding combinators.
Build decoders for your domain types. These produce CborDecoder values.
Run them with decode.
import Cbor.Decode as CD
type alias Person =
{ name : String, age : Int }
decodePerson : CD.CborDecoder ctx Person
decodePerson =
CD.keyedRecord CD.int String.fromInt Person
|> CD.required 0 CD.string
|> CD.required 1 CD.int
|> CD.buildKeyedRecord CD.IgnoreExtra
-- To run: CD.decode decodePerson someBytesNote: These decoders operate on a complete Bytes value. They cannot
incrementally decode a stream of chunks as they arrive. If you are receiving
CBOR data in multiple chunks (e.g. from a network stream), you must first
concatenate all chunks into a single Bytes value before calling decode.
If incremental / streaming decoding becomes necessary, it can be added in a
future release.
Run a CborDecoder on some Bytes.
Convert an Error to a human-readable string.
Provide a function to display context labels. If your decoder does not use
inContext, you can pass always "".
A decoder that always succeeds with the given value, consuming no bytes.
A decoder that always fails with the given error.
Transform the result of a decoder.
Combine two decoders sequentially.
Combine three decoders sequentially.
Combine four decoders sequentially.
Combine five decoders sequentially.
Chain decoders. The continuation is chosen based on the first result.
Try multiple decoders in order. Each decoder sees the same initial byte.
Apply a decoder producing a function to a decoder producing an argument.
succeed Point
|> keep float
|> keep floatDecode and discard a value, keeping the previous result.
Deferred construction for recursive decoders.
Add context to a decoder for better error messages.
If the decoder fails, the error will be wrapped with the given label and the byte offset where decoding started.
decodePerson : CD.CborDecoder String Person
decodePerson =
CD.inContext "Person"
(CD.keyedRecord CD.int String.fromInt Person
|> CD.required 0 (CD.inContext "name" CD.string)
|> CD.required 1 (CD.inContext "age" CD.int)
|> CD.buildKeyedRecord CD.IgnoreExtra
)Decode a CBOR integer (major types 0 and 1).
Fails if the absolute value exceeds 2^52. For larger values, use bigInt.
Decode any CBOR integer, including bignums (tags 2 and 3).
Returns ( Sign, Bytes ) where Bytes is the minimal big-endian
representation of the unsigned argument.
Decode a CBOR float (major type 7, additional info 25/26/27).
Decode a CBOR boolean (0xF4 for false, 0xF5 for true).
Decode a CBOR null (0xF6), returning the provided default value.
Decode a CBOR undefined (0xF7), returning the provided default value.
Decode a nullable value: CBOR null (0xF6) or undefined (0xF7) become
Nothing, anything else is decoded with the inner decoder and wrapped
in Just.
Decode a CBOR simple value (major type 7).
Returns the simple value number. Rejects booleans (20–21), null (22), undefined (23), and floats (25–27) which have their own decoders.
Decode a CBOR text string (major type 3).
Concatenates indefinite-length chunks transparently.
Decode a CBOR byte string (major type 2).
Concatenates indefinite-length chunks transparently.
Decode a CBOR array (major type 4) where all elements use the same decoder.
Handles both definite and indefinite-length arrays. The element decoder
must be an Item (i.e. consume bytes); using succeed or fail directly
as an element decoder will produce a NonConsumingDecoder error.
Decode a CBOR map (major type 5) as a list of key-value pairs.
Handles both definite and indefinite-length maps.
Decode a CBOR map (major type 5) into a Dict.
The key decoder must produce comparable values.
Decode the next map entry, expecting a specific key.
Reads the key, compares via ==, then decodes the value on match.
Fails on mismatch.
Fold over map entries with a handler for each key.
Reads the map header, then loops through entries. For each entry: decode the key, call the handler with the key and current accumulator. The handler decodes the value and returns the updated accumulator.
Important: the handler MUST decode exactly one value per call.
handler key acc =
case key of
0 ->
map (\name -> { acc | name = name }) string
1 ->
map (\age -> { acc | age = age }) int
_ ->
map (\_ -> acc) itemSkipDecode a CBOR tag (major type 6), returning the Tag value.
Does not decode the enclosed item. Useful with andThen to branch on
the tag number and then decode the body accordingly.
Decode a tagged CBOR value (major type 6).
Expects a specific tag, then decodes the enclosed item.
Opaque builder for decoding CBOR arrays into Elm values.
Uses SimpleBuilder when all fields are element (fast path via keep),
or CountedBuilder when optionalElement is used (counts remaining items).
Start building a record decoder with the constructor function.
record Point
|> element float
|> element float
|> buildRecord IgnoreExtraDecode one array element, applying it to the constructor.
Decode an optional array element, using the default if no items remain.
Optional elements must be at the end of the array.
What to do when the CBOR array has more elements than the builder expects.
IgnoreExtra — silently skip trailing elements (forward-compatible).FailOnExtra — fail with TooManyElements (strict validation).Finalize a record builder into a decoder.
Reads the array header, runs the builder pipeline, and handles any
extra elements according to the ExtraElements strategy.
Opaque builder for decoding CBOR maps into Elm values.
Start building a keyed record decoder.
keyedRecord int String.fromInt Person
|> required 0 string
|> required 1 int
|> buildKeyedRecord IgnoreExtraDecode a required map field by key.
The key order in the builder must match the key order in the CBOR data.
Decode an optional map field by key.
If the next key doesn't match, the key is stashed for the next step and the default value is used.
Finalize a keyed record builder into a decoder.
Reads the map header, runs the builder pipeline, and verifies all entries were consumed.
Builder for decoding CBOR maps into records regardless of key order.
Unlike KeyedRecordBuilder which requires keys in the declared order,
this builder uses a Dict to dispatch on each key. Unknown keys are
handled according to the ExtraElements policy.
The trade-off: the caller must define an accumulator type and a finalize function, since the constructor-threading pattern requires ordered application.
Start building an unordered record decoder.
type alias PersonAcc =
{ name : Maybe String, age : Maybe Int }
decodePerson : CborDecoder ctx Person
decodePerson =
unorderedRecord int { name = Nothing, age = Nothing }
|> onKey 0 string (\v acc -> { acc | name = Just v })
|> onKey 1 int (\v acc -> { acc | age = Just v })
|> buildUnorderedRecord IgnoreExtra
(\acc -> Maybe.map2 Person acc.name acc.age)Register a field handler for a specific key.
When the key is encountered during decoding, the value decoder runs and the setter updates the accumulator.
Finalize an unordered record builder into a decoder.
Reads the map header, scans all entries dispatching to registered handlers,
then calls the finalize function. If finalize returns Nothing, decoding
fails with TooFewElements.
Note: uses O(N) stack depth where N is the number of map entries. Safe for typical CBOR maps (up to a few hundred entries).
Decode a CBOR array header, returning the count or Nothing for
indefinite-length.
Decode a CBOR map header, returning the entry count or Nothing for
indefinite-length.
Consume a break byte (0xFF).
Use this after decoding a known number of elements from an indefinite-length container.
Decode items until a break byte (0xFF) is encountered.
Use this after reading an indefinite-length header with arrayHeader or
mapHeader. The element decoder must be Item (i.e. consume bytes).
Decode any well-formed CBOR data item into a CborItem.
This is the full CBOR parser. Handles all major types, nested structures, tags, indefinite-length containers, etc.
If you need the raw Bytes for an item, decoding with item then
re-encoding with Cbor.Encode.item is fast enough in practice
(on par or faster than a dedicated byte-slicing decoder).
Skip any well-formed CBOR data item without allocating anything.
This is the fastest way to move past a CBOR item. Uses BD.skipBytes
throughout, staying entirely in the fast lane.
Decode any well-formed CBOR data item and return its raw bytes.
Internally this decodes the item with item and re-encodes it
with Cbor.Encode.item. There is no way to slice the source bytes directly,
which would be faster in general due to allocation costs of small bytes slices.
Opaque CBOR decoder.
Internally uses two variants:
Itemreceives a pre-read initial byte (the CBOR byte encoding major type + additional info), andPureconsumes no initial byte (used only bysucceedandfail). This split lets combinators route the initial byte to the first decoder that needs it, and lets collection decoders detect indefinite-length break bytes on the fast path withoutBD.oneOf.