StatifierUI.Value (StatifierUI v0.3.0)

Copy Markdown View Source

Codec for ADR-0005's JSON encoding of the value domain predicator's Predicator.Types module defines (its value/0 type).

decode/1 reads a JSON-decoded term (as produced by the stdlib JSON module) and recovers the predicator value it encodes, resolving the $-prefixed one-key tagged shapes ADR-0005 reserves: $undefined, $date, $datetime, $duration, plus $redacted, which ADR-0012 adds to the reserved set. encode/1 is the inverse, present so the codec is round-trip testable; it is not wired to any writer.

$redacted decodes to the dedicated atom :redacted (operator ruling of 2026-08-29, recorded on ADR-0012). It is deliberately not :undefined: absence and redaction are different claims, and collapsing them is the failure ADR-0012 exists to prevent.

JSON-native values (booleans, numbers, strings, nil, lists, ordinary maps) map to themselves in both directions. JSON null always decodes to nil - a present, null value - never to the :undefined sentinel, which has its own tagged encoding because absence has no positional spelling inside a list or map value.

Durations are the one value that does not round-trip identically. encode/1 always writes all eight units, filling absent ones with 0, which is predicator's own contract rather than a widening of it: Predicator.Duration.new/1 fills every unit it was not given, and since predicator 9.0 the expression evaluator seeds all eight too, so Predicator.evaluate("3d") carries milliseconds: 0 like the rest (px-69c). StatifierUI.Shape.duration?/1 nonetheless recognizes any non-empty subset of the eight units, and that tolerance is viewer-side - a statement about the value streams this repository renders but did not produce, not about what predicator emits. A subset map therefore re-decodes as canonical rather than identical to the input, and semantically equal to it.

encode/1 is closed over predicator's value domain (ADR-0005): the JSON-native scalars, Date, DateTime, durations, string- or atom-keyed maps, and lists of the above. A term outside that domain - a bare atom other than nil, :undefined, :redacted, true, or false, a pid, tuple, reference, port, function, or a struct other than Date or DateTime - is rejected with {:error, {:unsupported_value, term}} rather than passed through or allowed to raise.

Summary

Functions

Decodes a JSON-decoded term into a predicator value.

Encodes a predicator value into its ADR-0005 JSON-native term.

Functions

decode(map)

@spec decode(term()) :: {:ok, term()} | {:error, term()}

Decodes a JSON-decoded term into a predicator value.

Recurses through lists and map values. A one-key map whose key starts with $ and is not one of the five reserved tags is an error: ADR-0005 reserves the whole one-key $-prefixed shape, so an unrecognized tag is a spec violation on the producer's side rather than a host map to pass through. A multi-key map containing a $-prefixed key is an ordinary host map.

Examples

iex> StatifierUI.Value.decode(1999)
{:ok, 1999}

iex> StatifierUI.Value.decode(nil)
{:ok, nil}

iex> StatifierUI.Value.decode(%{"$undefined" => true})
{:ok, :undefined}

iex> StatifierUI.Value.decode(%{"$redacted" => true})
{:ok, :redacted}

encode(date)

@spec encode(term()) :: {:ok, term()} | {:error, term()}

Encodes a predicator value into its ADR-0005 JSON-native term.

The inverse of decode/1. Not wired to any writer; present so the codec is round-trip testable.

A term outside predicator's closed value domain - a bare atom other than nil, :undefined, :redacted, true, or false, a pid, tuple, reference, port, function, or a struct other than Date or DateTime - returns {:error, {:unsupported_value, term}}. This function never raises.

Examples

iex> StatifierUI.Value.encode(:undefined)
{:ok, %{"$undefined" => true}}

iex> StatifierUI.Value.encode(:redacted)
{:ok, %{"$redacted" => true}}

iex> StatifierUI.Value.encode(1999)
{:ok, 1999}