Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
sway
nix source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
Nix 2.26.3
Nix 2.27.1
Nix 2.28.3
Nix 2.29.0
Options
Source code
let schema = { additionalProperties = false; properties = { age = { maximum = 100; minimum = 18; type = "integer"; }; contact = { additionalProperties = false; properties = { address = { additionalItems = { type = "string"; }; maxItems = 4; minItems = 3; prefixItems = [ { description = "Street address"; minLength = 5; type = "string"; } { description = "House number"; minimum = 1; type = "integer"; } { description = "Postal code (5 or 9 digits)"; pattern = "^[0-9]{5}(-[0-9]{4})?$"; type = "string"; } ]; type = "array"; }; primary = { description = "Primary phone number"; pattern = "^\\+?[1-9][0-9]{1,14}$"; type = "string"; }; secondary = { description = "Secondary phone number"; pattern = "^\\+?[0-9]{1,14}$"; type = "string"; }; }; required = [ "primary" "secondary" ]; type = "object"; }; email = { maxLength = 255; pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"; type = "string"; }; id = { description = "UUID for unique identification"; pattern = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"; type = "string"; }; isActive = { type = "boolean"; }; name = { maxLength = 50; minLength = 3; pattern = "^[A-Za-z\\s]+$"; type = "string"; }; nothing = { type = "null"; }; pprop = { additionalProperties = false; patternProperties = { "^bool-[0-9]+$" = { type = "boolean"; }; "^eint-[0-9]+$" = { maximum = 10000; minimum = 0; type = "integer"; }; "^prop-[0-9]+$" = { maxLength = 100; minLength = 2; type = "string"; }; }; type = "object"; }; roles = { items = { enum = [ "admin" "editor" "viewer" "manager" "developer" "analyst" "tester" ]; type = "string"; }; maxItems = 15; minItems = 1; type = "array"; uniqueItems = true; }; score = { maximum = 100; minimum = 0; multipleOf = 3; type = "integer"; }; status = { enum = [ "active" "inactive" "pending" "suspended" ]; type = "string"; }; tags = { contains = { pattern = "^tag-.+"; type = "string"; }; items = { maxLength = 20; type = "string"; }; maxContains = 5; minContains = 1; type = "array"; }; }; required = [ "id" "name" "age" "roles" "tags" "pprop" "status" "isActive" "nothing" "contact" ]; type = "object"; }; data = { age = 45; contact = { address = [ "1234 Maple Drive" 56 "90210" "more extra info ;)" ]; primary = "+12025550123"; secondary = "+12025550124"; }; email = "jonathan.smith@example.com"; id = "123e4567-e89b-12d3-a456-426614174000"; isActive = true; name = "Jonathan"; nothing = null; pprop = { bool-7 = true; bool-8 = false; eint-45 = 6789; eint-99 = 123; prop-1 = "configuration"; prop-23 = "metadata"; }; roles = [ "admin" "developer" "manager" "tester" ]; score = 9; status = "active"; tags = [ "tag-projectX" "tag-sprint42" "tag-backend" "tag-core" ]; }; throwMsg = field: msg: throw "Validation error in `${field}`: ${msg}"; json2nix_t = jtype: { "string" = "string"; "integer" = "int"; "number" = ["int" "float"]; "array" = "list"; "object" = "set"; "boolean" = "bool"; "null" = "null"; }.${jtype} or ""; checkString = name: schema: value: let len = builtins.stringLength value; in assert (!schema ? minLength || len >= schema.minLength) || throwMsg name "must have minLength ${toString schema.minLength}"; assert (!schema ? maxLength || len <= schema.maxLength) || throwMsg name "must have maxLength ${toString schema.maxLength}"; assert (!schema ? enum || builtins.elem value schema.enum) || throwMsg name "must be one of ${builtins.concatStringsSep ", " schema.enum}"; assert (!schema ? pattern || builtins.match schema.pattern value != null) || throwMsg name "must match pattern `${schema.pattern}`"; value; checkNumber = name: schema: value: let typeStr = builtins.typeOf value; mod = a: b: a - b * (a / b); in assert (schema.type == "integer" -> typeStr == "int") || throwMsg name "must be an integer"; assert (schema.type == "number" -> typeStr == "float" || typeStr == "int") || throwMsg name "must be a number"; assert (!schema ? minimum || value >= schema.minimum) || throwMsg name "must be >= ${toString schema.minimum}"; assert (!schema ? maximum || value <= schema.maximum) || throwMsg name "must be <= ${toString schema.maximum}"; assert (!schema ? exclusiveMinimum || value > schema.exclusiveMinimum) || throwMsg name "must be > ${toString schema.exclusiveMinimum}"; assert (!schema ? exclusiveMaximum || value < schema.exclusiveMaximum) || throwMsg name "must be < ${toString schema.exclusiveMaximum}"; assert (!schema ? multipleOf || (mod value schema.multipleOf) == 0) || throwMsg name "must be a multiple of ${toString schema.multipleOf}"; assert (!schema ? enum || builtins.elem value schema.enum) || throwMsg name "must be one of ${builtins.concatStringsSep ", " (map toString schema.enum)}"; value; checkTuple = name: schema: value: let len = builtins.length value; tupleLen = builtins.length schema.prefixItems; addl = schema.additionalItems or true; prefixLen = if len < tupleLen then len else tupleLen; validatedPrefix = builtins.genList (i: validate (builtins.elemAt schema.prefixItems i) "${name}[${toString i}]" (builtins.elemAt value i) ) prefixLen; extraVals = if len <= tupleLen then [] else if addl == true then builtins.genList (i: builtins.elemAt value (i + tupleLen)) (len - tupleLen) else if builtins.isAttrs addl then builtins.genList (i: validate addl "${name}[${toString (i + tupleLen)}]" (builtins.elemAt value (i + tupleLen)) ) (len - tupleLen) else throw "additional items not allowed, but found ${toString (len - tupleLen)} extra items"; in validatedPrefix ++ extraVals; checkArray = name: schema: value: let len = builtins.length value; itemVals = if schema ? prefixItems then checkTuple name schema value else builtins.map (v: validate schema.items "${name}[]" v) value; matches = if schema ? contains then builtins.filter (v: (builtins.typeOf (validate schema.contains name v)) == (json2nix_t schema.contains.type)) value else []; minC = if schema ? minContains then schema.minContains else 1; maxC = if schema ? maxContains then schema.maxContains else len; in assert (!schema ? minItems || len >= schema.minItems) || throwMsg name "must have at least ${toString schema.minItems} items"; assert (!schema ? maxItems || len <= schema.maxItems) || throwMsg name "must have at most ${toString schema.maxItems} items"; assert (!schema ? uniqueItems || !schema.uniqueItems || len == builtins.length (builtins.foldl' (acc: x: if builtins.elem x acc then acc else acc ++ [x]) [] itemVals)) || throwMsg name "must have unique items"; assert (!schema ? contains || (builtins.length matches >= minC && builtins.length matches <= maxC)) || throwMsg name "must contain between ${toString minC} and ${toString maxC} items matching contains schema"; matches; checkObject = name: schema: value: let required = schema.required or []; props = schema.properties or {}; patterns = schema.patternProperties or {}; canExtra = schema.additionalProperties or true; keys = builtins.attrNames value; validKeys = (builtins.attrNames props) ++ builtins.concatLists ( builtins.map (pat: builtins.filter (k: builtins.match pat k != null) keys ) (builtins.attrNames patterns) ); propsValidated = builtins.mapAttrs (p: s: if value ? ${p} then validate s "${name}.${p}" value.${p} else null ) props; patternsValidated = builtins.listToAttrs ( builtins.concatLists ( builtins.map (pat: builtins.map (k: { name = k; value = validate patterns.${pat} "${name}.${k}" value.${k}; }) (builtins.filter (k: builtins.match pat k != null) keys) ) (builtins.attrNames patterns) ) ); missingRequired = builtins.filter (f: !(value ? ${f})) required; extraKeys = builtins.filter (k: !(builtins.elem k validKeys)) keys; in assert (builtins.all (f: value ? ${f}) required) || throwMsg name "missing required field(s): ${builtins.concatStringsSep ", " missingRequired}"; assert (canExtra || builtins.all (k: builtins.elem k validKeys) keys) || throwMsg name "contains undeclared or not matched properties. Allowed: ${builtins.concatStringsSep ", " validKeys}, got ${builtins.concatStringsSep ", " extraKeys}"; propsValidated // patternsValidated; validate = schema: name: value: assert (schema ? type) || throwMsg name "schema.type is missing"; assert (json2nix_t schema.type != "") || throwMsg name "schema.type is of unsupported type `${schema.type}`"; if schema.type == "string" then checkString name schema value else if schema.type == "integer" || schema.type == "number" then checkNumber name schema value else if schema.type == "array" then checkArray name schema value else if schema.type == "object" then checkObject name schema value else if schema.type == "null" then value else if schema.type == "boolean" then value else throwMsg name "unsupported schema type: ${schema.type}"; in # Snix-bolt doesn't show full evaluation values toJSON/fromJSON https://bolt.snix.dev/ builtins.fromJSON (builtins.toJSON (validate schema "root" data))
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree