cue implemented in pure jq.
CUE is a genuinely useful tool for data validation and configuration -- but pulling in a Go binary just to validate some JSON is overkill when jq is already everywhere.
j for jq, cue for CUE.
Add jcue.jq somewhere on your jq library path and include "jcue" in any filter. validate($schema) returns its input unchanged on success and throws an error on failure:
$ echo '{"name": "Alice", "age": 30}' | jq -L /path/to/jcue 'include "jcue"; validate({"name": {"$cue": "string"}, "age": {"$cue": "int"}})'
{
"name": "Alice",
"age": 30
}
$ echo '{"name": "Alice", "age": "thirty"}' | jq -L /path/to/jcue 'include "jcue"; try validate({"name": {"$cue": "string"}, "age": {"$cue": "int"}}) catch .'
"expected int, got string"from_cue($text) parses a CUE type expression and returns a jcue schema, letting you write schemas in real CUE syntax instead of as JSON. This is the jq equivalent of cue vet -- pipe any JSON through validate(from_cue(...)) to check it against a CUE schema:
$ echo '{"name": "Alice", "age": 30}' | jq -L /path/to/jcue 'include "jcue"; validate(from_cue("{name: string, age: int}"))'
{
"name": "Alice",
"age": 30
}
$ echo '{"name": 42}' | jq -L /path/to/jcue 'include "jcue"; try validate(from_cue("{name: string}")) catch .'
"expected string, got number"Schemas in .cue files load with --rawfile, and multiple files validate with a single glob:
$ cat schema.cue
#Config: {
host: string
port: int & >= 1 & <= 65535
}
$ jq -L /path/to/jcue --rawfile schema schema.cue 'include "jcue"; validate(from_cue($schema))' config.json
$ jq -L /path/to/jcue --rawfile schema schema.cue 'include "jcue"; validate(from_cue($schema))' configs/*.jsonfrom_cue handles the full type-expression subset: type keywords (string, int, float, bool, bytes, number), top (_) and bottom (_|_), constraints (>= N, != X, =~ "pat"), conjunction (A & B), disjunction (A | B), default markers (*val | type), open and close({...}) structs, and fixed/variable-length lists ([T, ...], [...T]). Top-level #Name: expr definition syntax is also accepted.
Schemas are plain JSON. Abstract types use {"$cue": "kind"} as a type marker; plain objects, arrays, and scalars are structural constraints ("$cue" is a reserved key and cannot appear as a struct field name).
| schema | accepts |
|---|---|
{"$cue": "top"} |
any value |
{"$cue": "bottom"} |
nothing (always fails) |
{"$cue": "null"} |
null |
{"$cue": "bool"} |
any boolean |
{"$cue": "string"} |
any string |
{"$cue": "bytes"} |
bytes (string in JSON) |
{"$cue": "number"} |
any number |
{"$cue": "float"} |
any number (float bias) |
{"$cue": "int"} |
any integer |
{"$cue": ">=", "v": N} |
number ≥ N |
{"$cue": "<=", "v": N} |
number ≤ N |
{"$cue": ">", "v": N} |
number > N |
{"$cue": "<", "v": N} |
number < N |
{"$cue": "!=", "v": X} |
anything not equal to X |
{"$cue": "=~", "v": "pat"} |
string matching regex |
{"$cue": "!~", "v": "pat"} |
string not matching regex |
{"$cue": "&", "of": [...]} |
value matching all schemas (conjunction) |
{"$cue": "|", "of": [...]} |
value matching any schema (disjunction) |
{"$cue": "list", "elem": schema} |
variable-length list |
{"$cue": "close", "v": {...}} |
closed struct (no extra fields) |
plain object (no "$cue") |
open struct; field names suffixed ? are optional |
| plain array | fixed-length list (element schemas by position) |
| plain scalar | exact value match |