No matching definitions.

tur/schema

stdlib/schema.tur

runtime schema validation for untyped boundary data.

Schemas are first-class runtime values that describe the expected shape of data arriving at a dynamic boundary (an HTTP body, a config file, a channel message). Unlike tur/contract (which panics), schema-decode returns a Result so callers handle bad data gracefully, and it accumulates *all* violations with dot-separated field paths rather than failing fast.

Decoding is anchored on the tagged JSON node representation from tur/json (json/decode, #json(...)): those nodes carry a runtime type tag (0=null 1=bool 2=int 3=float 4=string 5=array 6=object), which is what makes genuine "expected :cstr, got :int" validation possible. Raw Turmeric int64 values carry no type tag, so the schema decoder reads the shape it needs from JSON nodes.

A schema value is a heap-allocated tagged struct:

{ int64_t kind; int64_t a; int64_t b; int64_t c; }

The kind is one of the SCHEMA_* discriminants; a/b/c are kind-specific. Object schemas are built incrementally (schema/object-new + schema/field) because a field list mixes :cstr keys with schema pointers, which a homogeneous Vec cannot hold.

Decoded result values:

scalar  -> the underlying value (cstr ptr / int / float bits / bool)
literal -> the matched value
object  -> the validated JSON object node (use json/get to read fields)
array   -> a freshly allocated Vec of decoded element values
optional, union, transform, rec -> the inner decoded value

Errors are a Vec of SchemaError; schema-decode returns Result<ptr<void>, Vec<SchemaError>>.

Since: Phase SC0

def

SCHEMA_STR

(def SCHEMA_STR 0)

discriminant for the :cstr scalar schema.

def

SCHEMA_INT

(def SCHEMA_INT 1)

discriminant for the :int scalar schema.

def

SCHEMA_FLOAT

(def SCHEMA_FLOAT 2)

discriminant for the :float scalar schema.

def

SCHEMA_BOOL

(def SCHEMA_BOOL 3)

discriminant for the :bool scalar schema.

def

SCHEMA_NIL

(def SCHEMA_NIL 4)

discriminant for the null schema.

def

SCHEMA_LITERAL

(def SCHEMA_LITERAL 5)

discriminant for an exact-value schema.

def

SCHEMA_OBJECT

(def SCHEMA_OBJECT 6)

discriminant for an object (field-list) schema.

def

SCHEMA_ARRAY

(def SCHEMA_ARRAY 7)

discriminant for a homogeneous array schema.

def

SCHEMA_OPTIONAL

(def SCHEMA_OPTIONAL 8)

discriminant for an optional (nullable) schema.

def

SCHEMA_UNION

(def SCHEMA_UNION 9)

discriminant for a first-match union schema.

def

SCHEMA_TRANSFORM

(def SCHEMA_TRANSFORM 10)

discriminant for a decode-then-map schema.

def

SCHEMA_REC

(def SCHEMA_REC 11)

discriminant for a recursive (self-referential) schema.

def

SCHEMA_ALWAYS

(def SCHEMA_ALWAYS 12)

discriminant for a schema that always succeeds (backs pure).

def

SCHEMA_NEVER

(def SCHEMA_NEVER 13)

discriminant for a schema that always fails (backs empty).

def

SCHEMA_AP

(def SCHEMA_AP 14)

discriminant for the Validation applicative (apply + accumulate).

def

SCHEMA_FIELD

(def SCHEMA_FIELD 15)

discriminant for an extract-one-field schema.

def

SCHEMA_AP_FAT

(def SCHEMA_AP_FAT 16)

discriminant for a closure-aware applicative apply.

defn

schema/str

(schema/str :int)

a schema that matches JSON strings (:cstr).

A scalar string schema; decodes to the underlying cstr.

(schema-decode (schema/str) (json/string "hi"))  ; => ok("hi")

Since: Phase SC0

defn

schema/int

(schema/int :int)

a schema that matches JSON integers (:int).

A scalar int schema; decodes to the underlying int.

(schema-decode (schema/int) (json/int 42))  ; => ok(42)

Since: Phase SC0

defn

schema/float

(schema/float :int)

a schema that matches JSON floats (:float).

A scalar float schema; decodes to the underlying float bits.

Since: Phase SC0

defn

schema/bool

(schema/bool :int)

a schema that matches JSON booleans (:bool).

A scalar bool schema; decodes to 0 or 1.

Since: Phase SC0

defn

schema/nil

(schema/nil :int)

a schema that matches JSON null.

A null schema; decodes to 0.

Since: Phase SC0

defn

schema/literal-int

(schema/literal-int [v :int] :int)

a schema matching one exact integer value.

vthe required int value

A literal schema; decodes to v only when the node is exactly v.

(schema-decode (schema/literal-int 7) (json/int 7))  ; => ok(7)

Since: Phase SC0

defn

schema/literal-str

(schema/literal-str [v :cstr] :int)

a schema matching one exact string value.

vthe required cstr value

A literal schema; decodes to v only when the node string equals v.

(schema-decode (schema/literal-str "active") (json/string "active"))

Since: Phase SC0

defn

schema/object-new

(schema/object-new :int)

start an empty object schema.

An object schema with no fields yet; add fields with schema/field.

(schema/field (schema/field (schema/object-new)
                            "name" (schema/str))
              "age" (schema/int))

Since: Phase SC1

defn

schema/field

(schema/field [obj :int key :cstr inner :int] :int)

add a declared field to an object schema.

objan object schema (from schema/object-new)
keythe field name
innerthe schema the field value must satisfy. Wrap it in
schema/optional to allow the field to be absent or null.

The same object schema, with the field appended (mutated in place).

(schema/field (schema/object-new) "name" (schema/str))

Since: Phase SC1

defn

schema/array

(schema/array [elem :int] :int)

a schema for a homogeneous JSON array.

elemthe schema each element must satisfy

An array schema; decodes to a Vec of decoded element values.

(schema/array (schema/int))  ; matches [1, 2, 3]

Since: Phase SC2

defn

schema/optional

(schema/optional [inner :int] :int)

a schema where null / absent is acceptable.

innerthe schema a present, non-null value must satisfy

An optional schema; decodes to the inner value, or 0 when null. Inside schema/object, an optional field may also be absent entirely.

(schema/optional (schema/str))

Since: Phase SC2

defn

schema/union

(schema/union [arms :int] :int)

a schema where the first matching arm wins.

armsa Vec of schemas; tried left to right

A union schema; decodes to the value from the first arm that matches. If no arm matches, all arms' errors are reported.

(schema/union (vec-of (schema/int) (schema/str)))

Since: Phase SC2

defn

schema/transform

(schema/transform [inner :int ^fat f] :int)

decode with a schema, then map the result.

innerthe schema to decode with
fa function or *capturing closure* applied to the decoded value
(only on success); carried as a fat closure (^fat)

A transform schema; decodes to (f decoded-inner).

(schema/transform (schema/int) (fn [x] (* x 2)))
(let [k 100] (schema/transform (schema/int) (fn [x] (+ x k))))  ; captures k

Since: Phase SC2

defn

schema/rec

(schema/rec [f] :int)

a self-referential schema for recursive data.

fa function taking the recursive schema itself (self) and
returning the schema body, e.g.
(fn [self] (schema/field (schema/object-new)
"children" (schema/array self)))

A recursive schema. The body is forced (by calling f with self) on first decode and memoized thereafter.

(schema/rec (fn [self]
  (schema/field
    (schema/field (schema/object-new) "value" (schema/int))
    "children" (schema/array self))))

Since: Phase SC4

defn

schema/kind

(schema/kind [s :int] :int)

return the SCHEMA_* discriminant of a schema value.

sa schema value

The integer discriminant (SCHEMA_STR .. SCHEMA_REC).

Since: Phase SC0

defn

schema/always

(schema/always [value :int] :int)

a pure schema that ignores its input, decoding to a fixed value.

valuethe value (an int-carrier; e.g. an int, a cstr ptr, or a
top-level function pointer) the schema always decodes to

An always-succeeds schema; decoding it never inspects the node.

(schema-decode! (schema/always 42) (json/null))  ; => 42

Since: Phase SC7

defn

schema/never

(schema/never [message :cstr] :int)

the empty schema: always fails with a fixed message (identity of schema/union).

messagethe failure message reported at the current path

An always-fails schema.

(schema-decode (schema/never "no match") (json/int 1))  ; => err

Since: Phase SC7

defn

schema/ap

(schema/ap [sf :int sa :int] :int)

the Validation applicative: apply sf's decoded function to sa's decoded value, accumulating both arms' errors.

Decodes the function arm sf and the argument arm sa against the *same* input, concatenates their errors left-to-right, and applies the decoded function to the decoded argument only when both arms succeed.

sfa schema decoding to a unary function pointer (a/the result of
schema/always over a top-level, non-capturing function)
saa schema decoding to that function's argument

An applicative schema decoding to (f a). NOTE: the decoded function is applied via a direct C call, so it must be a top-level (non-capturing) function -- multi-argument currying needs closures and is deferred.

;; double-it is a top-level (defn double-it [x] (* x 2))
(schema/ap (schema/always double-it)
           (schema/field-of "n" (schema/int)))

Since: Phase SC7

defn

schema/field-of

(schema/field-of [key :cstr inner :int] :int)

decode a JSON object and extract one key's decoded value via an inner schema.

Yields the key's decoded value (not the whole object), so it composes with schema/ap to assemble values field by field.

keythe object key to extract
innerthe schema the key's value must satisfy. Wrap in schema/optional
to allow the key to be absent.

A field-extract schema decoding to the inner decoded value.

(schema/field-of "age" (schema/int))  ; on {"age":30} => 30

Since: Phase SC7

defn

schema/fmap

(schema/fmap [inner :int ^fat f] :int)

the Functor map for schemas: decode with inner, then map the result with f (alias for schema/transform).

innerthe schema to decode with
fa function or capturing closure applied to the decoded value on
success; carried as a fat closure (^fat)

A schema decoding to (f decoded-inner).

(schema/fmap (schema/int) double-it)

Since: Phase SC7

defn

schema/alt

(schema/alt [a :int b :int] :int)

the Alternative <|>: a two-arm first-match union (try a, then b).

athe first schema to try
bthe schema to try if a does not match

A union schema over the two arms.

(schema/alt (schema/int) (schema/str))

Since: Phase SC7

defn

schema/ap-fat

(schema/ap-fat [sf :int sa :int] :int)

closure-aware Validation applicative (kind 16).

Like schema/ap, but applies the decoded function arm to the decoded argument through the fat-closure protocol (TUR_APPLY1) rather than a raw C call, so the function arm may be a *capturing* closure -- e.g. the partially-applied curried constructor produced by fmap ctor field when assembling a struct field by field. Same accumulating error semantics as schema/ap. This is the combinator the Applicative [Schema] instance's ap method is built on.

sfa schema decoding to a unary fat-closure handle
saa schema decoding to that closure's argument

An applicative schema decoding to (f a).

Since: Phase SC7

defstruct

Schema

(defstruct Schema [A] (raw :int))

phantom wrapper over an int-carrier schema, for HKT dispatch.

Aphantom: the type a successful decode produces
rawthe underlying SC0 schema pointer (int-carrier)

Since: Phase SC7

definstance

Functor[Schema]

(definstance Functor [Schema])

map a function over a schema's decoded value.

definstance

Applicative[Schema]

(definstance Applicative [Schema])

Validation applicative: pure + accumulating ap.

pure lifts a value (schema/always); ap applies a decoded function arm to a decoded argument arm via the closure-aware schema/ap-fat, accumulating both arms' errors (Validation, not fail-fast). Monad is deliberately omitted: the lawful fail-fast >>= contradicts this accumulating <*> (see schema/ap).

definstance

Alternative[Schema]

(definstance Alternative [Schema])

first-match choice (alt-or) + the empty schema.

defn

schema-error-path

(schema-error-path [e :int] :cstr)

the dot-separated field path of a SchemaError.

ea SchemaError value

The path cstr, e.g. "user.address.zip" (empty string at the root).

Since: Phase SC3

defn

schema-error-text

(schema-error-text [e :int] :cstr)

the human-readable message of a SchemaError.

ea SchemaError value

The message cstr, e.g. "expected :cstr, got :int".

Since: Phase SC3

defn

schema-error-count

(schema-error-count [errs :int] :int)

number of errors in an error Vec.

errsa Vec of SchemaError (the err payload of schema-decode)

The error count (0 if errs is null).

Since: Phase SC3

defn

schema-error-at

(schema-error-at [errs :int i :int] :int)

the i-th SchemaError in an error Vec.

errsa Vec of SchemaError
izero-based index

The SchemaError at index i (0 if out of bounds).

Since: Phase SC3

defn

schema-error-message

(schema-error-message [errs :int] :cstr)

format an error Vec into one human-readable cstr.

errsa Vec of SchemaError

A newline-separated string, one "path: message" line per error (a root-level error omits the leading "path: "). Caller owns the cstr.

(schema-error-message errs)  ; => "user.age: expected :int, got :cstr"

Since: Phase SC3

defn

sch-vpush-

(sch-vpush- [v :ptr x :int] :void)

validate a JSON node against a schema.

schemaa schema value (from the constructors above)
nodea JSON node (from json/decode or #json(...))

Result<ptr<void>, Vec<SchemaError>>. On success, ok holds the decoded value (see the module docstring for the per-kind result shape). On failure, err holds a Vec of every accumulated SchemaError -- decoding does not stop at the first violation.

(let [s (schema/field (schema/object-new) "age" (schema/int))
      r (schema-decode s (json/decode "{\"age\":1}"))]
  (schema-decode-ok? r))  ; => true

Since: Phase SC1

defn

sch-push-err-

(sch-push-err- [errs :ptr path :cstr msg :cstr val :int] :void)
defn

sch-mkpath-

(sch-mkpath- [base :cstr key :cstr] :ptr)
defn

sch-mkidx-

(sch-mkidx- [base :cstr idx :int] :ptr)
defn

sch-tyname-

(sch-tyname- [jtype :int] :cstr)
defn

sch-want-name-

(sch-want-name- [kind :int] :cstr)
defn

sch-jtype-

(sch-jtype- [n :int] :int)
defn

sch-jpayload-

(sch-jpayload- [n :int] :int)
defn

sch-decode-rec-

(sch-decode-rec- [schema :int node :int path :cstr errs :ptr] :int)
defn

schema-decode

(schema-decode [schema :int node :int] :int)
defn

schema-decode-ok?

(schema-decode-ok? [r :int] :bool)

whether a schema-decode Result succeeded.

rthe Result from schema-decode

true if decoding succeeded, false otherwise.

Since: Phase SC1

defn

schema-decode-value

(schema-decode-value [r :int] :int)

the decoded value of a successful Result.

rthe Result from schema-decode (must be ok)

The decoded value (see the module docstring for per-kind shape).

Since: Phase SC1

defn

schema-decode-errors

(schema-decode-errors [r :int] :int)

the error Vec of a failed Result.

rthe Result from schema-decode (must be err)

The Vec of SchemaError (0 if r is ok).

Since: Phase SC1

defn

schema-decode-report

(schema-decode-report [errs :int] :int)

internal: print an error Vec to stderr.

defn

schema-decode-abort

(schema-decode-abort [errs :int] :int)

internal: report an error Vec and raise.

A panic, not a bare abort(): with no catch boundary in scope the two are indistinguishable (both print and die), but a panic is what makes (catch-unwind (fn [] : T (decode! ...))) -- and with it the Result-returning #json-str?<T> reader -- able to recover from a schema violation instead of taking the process down. The errors are printed first, so the diagnostic detail survives either way.

defn

schema-decode!

(schema-decode! [schema :int node :int] :int)

decode against a schema, panicking on any violation.

schemaa schema value
nodea JSON node

The decoded value. Aborts with the formatted error message if the value does not satisfy the schema -- use only for trusted data / tests.

(schema-decode! (schema/int) (json/int 5))  ; => 5

Since: Phase SC1

defclass

HasSchema

(defclass HasSchema [a])

a type that knows how to validate-and-build itself from a tagged JSON node.

The single method, decode!, puts its dispatch type variable a only in the return position, so the concrete instance is selected from the *expected type at the use site* (an ascription (:: (decode! node) T) or a typed binding (let [x : T (decode! node)] ...)) rather than from any argument. This is return-type-directed dispatch (Phase RT): the elaborator unifies the method's a against the ascribed type and emits a direct call to the matching instance.

An instance pairs a schema (the wire shape) with a builder that turns the validated node into the typed value, e.g.

(definstance HasSchema [User]
  (decode! [node]
    (let [v (schema-decode! (user-schema) node)]
      (make-struct User (json/get-string (json/get! v "name"))
                        (json/get-int    (json/get! v "age"))))))

and is then driven by an ascription:

(:: (decode! (json/decode body)) User)   ; => a real User struct

decode! panics (via schema-decode!) on any schema violation; it is for trusted data and tests. For graceful handling, validate first with schema-decode against the type's schema and branch on schema-decode-ok?.

(decode! [node :int] :a)

Since: Phase SC5