Public API for DXN (Data eXchange Notation) — .dxn text and
.dxnb binary, per DXN.md (the normative format specification).
Four functions, one shared error type (Dextrin.Error):
decode/2/encode/2—.dxntext.decode_binary/2/encode_binary/2—.dxnbbinary.
Every DXN type decodes to a plain Elixir value where one exists
(integer, float, list, a plain map, ...) and to a small wrapper
struct where Elixir has nothing that fits without losing information
(Dextrin.Symbol, Dextrin.Tuple, Dextrin.OrderedMap, ...) — see
each value module under Dextrin.Value for the full list. symbol
and keyword in particular always wrap a String.t(), never an
Elixir atom: decoding untrusted DXN data can never be used to exhaust
the atom table.
struct is schema-dependent: without a compiled .dxns schema for a
given name it decodes to an opaque Dextrin.Struct; with one
(Dextrin.Schema.compile/3, passed in as registry:), field
enforcement happens automatically in both decode functions, and a
violation is an ordinary {:error, %Dextrin.Error{}}, never a raised
exception. encode/2/encode_binary/2 validate the other
direction the same way, automatically — see encode/2's own doc.
.dxnb's private CBOR tag block (200–214, mirrored in
Dextrin.Binary.Tags) is not IANA-registered — collision-free only
within dextrin-produced documents. Don't assume interop with some
other CBOR-based format that happens to also use a tag in that
range; it isn't reserved for DXN outside this library's own output.
Summary
Functions
Decodes .dxn text into a value. {:error, _} carries a single
Dextrin.Error normally, but a list when the underlying grammar
engine reports more than one (Ichor.Actions.evaluate/5's own
Ichor.Error.t() | [Ichor.Error.t()]).
Decodes a .dxnb binary into a value. Same trusted: opt as
decode/2 — see there for what it does and doesn't affect.
Encodes a value back to .dxn text.
Encodes a value to .dxnb binary. Same schema:/validate: opts
as encode/2.
Types
@type opts() :: [ registry: Dextrin.Registry.t(), schema: String.t(), validate: boolean(), pretty: boolean(), indent: non_neg_integer(), trusted: boolean() ]
Functions
@spec decode(String.t(), opts()) :: {:ok, term()} | {:error, Dextrin.Error.t() | [Dextrin.Error.t()]}
Decodes .dxn text into a value. {:error, _} carries a single
Dextrin.Error normally, but a list when the underlying grammar
engine reports more than one (Ichor.Actions.evaluate/5's own
Ichor.Error.t() | [Ichor.Error.t()]).
Decodes keyword as a real Elixir atom by default (trusted: true)
— see Dextrin.Registry.put_trusted/2's own doc for exactly what
this does and doesn't affect. Pass trusted: false for any source
you don't fully control; the default assumes a source you do
(your own config, your own application's data, ...), not arbitrary
untrusted/network input, where an unbounded String.to_atom/1 could
exhaust the atom table.
@spec decode_binary(binary(), opts()) :: {:ok, term()} | {:error, Dextrin.Error.t()}
Decodes a .dxnb binary into a value. Same trusted: opt as
decode/2 — see there for what it does and doesn't affect.
@spec encode(term(), opts()) :: {:ok, String.t()} | {:error, Dextrin.Error.t()}
Encodes a value back to .dxn text.
Single-line, minimal-whitespace by default — the smallest text this
value can round-trip through, with no line-wrapping or indentation
at all (Dextrin.Text.Printer). Pass pretty: true for multi-line,
indented output instead (Dextrin.Text.Formatter) — indent: then
sets the number of spaces per nesting level (default 2). Both
produce the exact same value on the way back through decode/2;
pretty/indent are a rendering choice, never a semantic one.
{:ok, value} = Dextrin.decode("%{x: 1, y: 2}")
Dextrin.encode(value)
#=> {:ok, "%{x:1,y:2}"}
Dextrin.encode(value, pretty: true)
#=> {:ok, "%{\n x: 1\n y: 2\n}"}
Dextrin.encode(value, pretty: true, indent: 4)
#=> {:ok, "%{\n x: 1\n y: 2\n}"}Automatically validates every Dextrin.Struct or registered
application struct found anywhere in value against its own schema
(Dextrin.Schema.validate_encode_tree/2), the same way decoding
checks every named struct unconditionally — set validate: false to
skip this (e.g. deliberately encoding data that doesn't conform, for
a test fixture or a pass-through/relay that shouldn't second-guess
data it isn't the origin of). A schema: opt additionally validates
value itself against that specific schema
(Dextrin.Schema.validate_encode/3) — the one case the automatic
walk can't cover on its own, a nameless plain map or struct at the
very top. Either check failing returns {:error, _} instead of
encoding a value that doesn't conform — regardless of pretty:,
since validation and rendering are independent concerns.
@spec encode_binary(term(), opts()) :: {:ok, binary()} | {:error, Dextrin.Error.t()}
Encodes a value to .dxnb binary. Same schema:/validate: opts
as encode/2.