gargamelle
Typed JSON decoding and JSON Schema 2020-12 emission from one definition.
Parse JSON with parse, then call validate or decode. Validation never
invokes user converters; decoding invokes them only after structural success.
Converters are trusted application code: exceptions are operational failures.
JavaScript and Erlang targets are supported on the
tested runtimes.
The Erlang target requires a separately compiled C regex helper.
Types
JSON syntax, nonfinite-number or non-scalar-string admission failure.
pub type AdmissionError {
AdmissionError(String)
}
Constructors
-
AdmissionError(String)
An invalid definition, constraint, default, or reference graph.
pub type ConstructionError {
ConstructionError(String)
}
Constructors
-
ConstructionError(String)
A structural validation failure. path is a JSON Pointer (empty at the root).
branches contains indexed union errors and matches identifies matching
branches. Categories and diagnostic wording may evolve before version 1.0.
pub type DataError {
DataError(
category: String,
path: String,
missing_key: option.Option(String),
branches: List(#(Int, List(DataError))),
matches: List(Int),
)
}
Constructors
-
DataError( category: String, path: String, missing_key: option.Option(String), branches: List(#(Int, List(DataError))), matches: List(Int), )
A completed definition, usable for validation, decoding and emission.
pub type Definition(a) =
Plan(a, Nil)
Json
opaqueAn opaque JSON value obtained through parse or schema_json.
Numbers are finite binary64 values; strings and keys contain Unicode scalars.
This type is distinct from gleam/json.Json; arbitrary runtime objects cannot
enter the validation boundary.
pub opaque type Json
JSON text layout. Compact uses canonical JSON; Pretty adds two-space
indentation without changing numbers, escaping or key order. Neither adds
a trailing newline. Empty arrays and objects stay on one line.
pub type JsonFormat {
Compact
Pretty
}
Constructors
-
Compact -
Pretty
A finite numeric endpoint. Inclusive endpoints accept equality.
pub type NumberBound {
Inclusive(Float)
Exclusive(Float)
}
Constructors
-
Inclusive(Float) -
Exclusive(Float)
Plan
opaqueA composable acceptance contract and typed conversion under recursion r.
Only completed definitions, Plan(a, Nil), can be validated, decoded or emitted.
Recursive builder references remain construction plans after the builder returns.
pub opaque type Plan(a, r)
Record
opaqueAn unfinished record consuming a curried constructor one field at a time.
pub opaque type Record(a, r)
Treatment of undeclared record keys. Strict rejects them; Open accepts
them but omits them from the constructed output.
pub type RecordPolicy {
Strict
Open
}
Constructors
-
Strict -
Open
Recursive
opaqueThe opaque recursion environment supplied to a self-recursive plan. User code cannot construct or execute this environment.
pub opaque type Recursive(a)
Tuple
opaqueAn unfinished exact-length tuple consuming a curried constructor by position.
pub opaque type Tuple(a, r)
Variant
opaqueA record branch for tagged, with its discriminator supplied automatically.
pub opaque type Variant(a, r)
Values
pub fn any_of(
defs: List(Plan(a, r)),
) -> Result(Plan(a, r), ConstructionError)
Accept one or more matching branches and decode with the first matching branch in list order. Empty unions fail construction.
pub fn array(def: Plan(a, r)) -> Plan(List(a), r)
Accept homogeneous arrays and decode every element in input order.
pub fn bounds(
def: Plan(a, r),
lower: option.Option(Int),
upper: option.Option(Int),
) -> Result(Plan(a, r), ConstructionError)
Intersect inclusive integer bounds, Unicode code-point string lengths,
homogeneous array lengths or dictionary key counts. None leaves a side
unbounded. Unsupported definitions and invalid intervals fail construction.
pub fn contains(
def: Plan(a, r),
matching: Plan(b, r),
minimum: Int,
maximum: option.Option(Int),
) -> Result(Plan(a, r), ConstructionError)
Require between minimum and maximum structurally matching array items. Preserve the original decoder; matching-definition converters never run.
pub fn decode(
def: Plan(a, Nil),
value: Json,
) -> Result(a, List(DataError))
Validate the entire input before running typed conversion. Invalid inputs run no converters; exceptions from trusted callbacks propagate.
pub fn defaulted(
r: Record(fn(a) -> b, r),
name: String,
child: Plan(a, r),
value: Json,
) -> Result(Record(b, r), ConstructionError)
Add a field with an admitted JSON default validated during construction. When absent, decode the default with the child converter. A present invalid value still fails; defaults do not mutate the input or bypass validation.
pub fn description(def: Plan(a, r), s: String) -> Plan(a, r)
Add a schema description without changing validation or decoding.
pub fn dictionary(def: Plan(a, r)) -> Plan(List(#(String, a)), r)
Accept objects whose values satisfy the child definition. Decode to a Gleam list of key/value pairs, preserving original keys rather than a JS object.
pub fn element(
t: Tuple(fn(a) -> b, r),
child: Plan(a, r),
) -> Tuple(b, r)
Append one required position, consuming one constructor argument.
pub fn exclude(
def: Plan(a, r),
forbidden: Plan(b, r),
) -> Plan(a, r)
Reject input accepted by a forbidden structural definition. Preserve the base output; forbidden-definition converters never run.
pub fn finish(
r: Record(a, r),
policy: RecordPolicy,
) -> Plan(a, r)
Finish a typed record with the chosen undeclared-key policy.
pub fn finish_tuple(t: Tuple(a, r)) -> Plan(a, r)
Close a tuple. Short arrays and extra positions are rejected.
pub fn if_then(
def: Plan(a, r),
condition: Plan(b, r),
consequence: Plan(c, r),
) -> Plan(a, r)
Validate the consequence only when the structural condition accepts input. Retain the base output; condition/consequence converters never run.
pub fn if_then_else(
def: Plan(a, r),
condition: Plan(b, r),
consequence: Plan(c, r),
alternative: Plan(d, r),
) -> Plan(a, r)
Validate exactly the selected structural branch, retaining the base decoder.
pub fn integer_constant(
v: Int,
) -> Result(Plan(Int, r), ConstructionError)
Accept exactly this safe integer; reject constants outside the safe range.
pub fn lift(def: Plan(a, Nil)) -> Plan(a, r)
Compose a completed definition inside a recursive builder. Its converter ignores the builder environment and retains its own recursion.
pub fn map(def: Plan(a, r), f: fn(a) -> b) -> Plan(b, r)
Transform decoded output without changing schema acceptance. The callback runs only during successful decoding and must return the declared output type.
pub fn named(
name: String,
def: Plan(a, r),
) -> Result(Plan(a, r), ConstructionError)
Name a definition for local $defs/$ref reuse. Names must start with an
ASCII letter and contain only ASCII letters, digits, underscores or hyphens.
Conflicting names fail when the composed graph is validated or emitted.
pub fn nullable(def: Plan(a, r)) -> Plan(option.Option(a), r)
Accept null as None, or decode the child as Some. This does not make
a record field optional.
pub fn number() -> Plan(Float, r)
Decode any admitted finite IEEE-754 number to Float, including integers. JSON parsing may round or underflow; decoding preserves the parsed value.
pub fn number_bounds(
def: Plan(Float, r),
lower: option.Option(NumberBound),
upper: option.Option(NumberBound),
) -> Result(Plan(Float, r), ConstructionError)
Intersect a number definition’s interval with finite inclusive/exclusive bounds. Reject inverted intervals and exclusive equal endpoints.
pub fn one_of(
defs: List(Plan(a, r)),
) -> Result(Plan(a, r), ConstructionError)
Accept exactly one structurally matching branch and decode with it. Overlapping matches are rejected. Empty unions fail construction.
pub fn optional(
r: Record(fn(option.Option(a)) -> b, r),
name: String,
child: Plan(a, r),
) -> Result(Record(b, r), ConstructionError)
Add an optional field, consuming an Option(a) constructor argument.
Absence is None; present values must satisfy the child, including null.
pub fn parse(text: String) -> Result(Json, AdmissionError)
Admit JSON text without invoking a definition or converter. Duplicate object keys use the parser’s last value; numeric values use IEEE-754 binary64. String values and object keys must contain only Unicode scalar values.
pub fn pattern(
def: Plan(String, r),
p: String,
) -> Result(Plan(String, r), ConstructionError)
Intersect an unanchored ECMAScript Unicode regular expression with a string definition. Anchor explicitly for whole-string matching. Invalid expressions and unsupported underlying definitions fail construction.
pub fn property_names(
def: Plan(a, r),
keys: Plan(String, r),
) -> Result(Plan(a, r), ConstructionError)
Constrain every own object key without changing decoded values or keys. The key definition is used for validation only; its converter never runs.
pub fn record(constructor: a) -> Record(a, r)
Start a record with a curried constructor. Each field consumes one argument;
call finish after supplying all fields. Multi-argument constructors need currying.
pub fn recursive(
name: String,
build: fn(Plan(a, Recursive(a))) -> Result(
Plan(a, Recursive(a)),
ConstructionError,
),
) -> Result(Plan(a, Nil), ConstructionError)
Build a guarded self-recursive definition, running the builder once. The supplied plan can compose inside the builder but cannot be decoded, validated or emitted independently, including after construction completes.
pub fn recursive_pair(
first_name: String,
second_name: String,
build: fn(
Plan(a, #(Recursive(a), Recursive(b))),
Plan(b, #(Recursive(a), Recursive(b))),
) -> Result(
#(
Plan(a, #(Recursive(a), Recursive(b))),
Plan(b, #(Recursive(a), Recursive(b))),
),
ConstructionError,
),
) -> Result(#(Plan(a, Nil), Plan(b, Nil)), ConstructionError)
Build two guarded recursive definitions with independent output types. Both builder plans share a typed pair environment; return both bodies once.
pub fn required(
r: Record(fn(a) -> b, r),
name: String,
child: Plan(a, r),
) -> Result(Record(b, r), ConstructionError)
Add a required field. Duplicate field names fail construction.
pub fn safe_integer() -> Plan(Int, r)
Accept integral numbers between -9007199254740991 and 9007199254740991.
pub fn schema_json(def: Plan(a, Nil)) -> Json
Return a JSON Schema 2020-12 document as Gargamelle’s opaque Json, not
gleam/json.Json. Unfinished or conflicting reference graphs fail operationally.
pub fn schema_string(
def: Plan(a, Nil),
format: JsonFormat,
) -> String
Return a JSON Schema 2020-12 string in the chosen layout, including local references.
pub fn string_enum(
values: List(String),
) -> Result(Plan(String, r), ConstructionError)
Accept one of the supplied strings. Empty or duplicate alternatives fail construction.
pub fn tagged(
discriminator: String,
variants: List(Variant(a, r)),
) -> Result(Plan(a, r), ConstructionError)
Construct a discriminated union of record variants. Empty lists, duplicate tags and fields redeclaring the discriminator are construction errors.
pub fn title(def: Plan(a, r), s: String) -> Plan(a, r)
Add a schema title without changing validation or decoding.
pub fn to_string(value: Json, format: JsonFormat) -> String
Serialize any admitted JSON value in the chosen layout. Negative zero
becomes 0. Compact is canonical; Pretty changes only whitespace.
pub fn tuple(constructor: a) -> Tuple(a, r)
Start an exact-length positional array with a curried typed constructor.
pub fn unique_items(
def: Plan(List(a), r),
) -> Result(Plan(List(a), r), ConstructionError)
Require structurally distinct JSON items in a direct homogeneous array definition. Uniqueness is checked before mapping, not on decoded outputs.
pub fn validate(
def: Plan(a, Nil),
value: Json,
) -> Result(Nil, List(DataError))
Validate an admitted value without invoking constructors or map callbacks. Unfinished/conflicting reference graphs and resource exhaustion are operational failures rather than data errors.
pub fn variant(
tag: String,
r: Record(a, r),
policy: RecordPolicy,
) -> Variant(a, r)
Finish a tagged record branch. Do not declare the discriminator as a field.
pub fn variant_description(
v: Variant(a, r),
s: String,
) -> Variant(a, r)
Describe a tagged branch without changing its acceptance contract.