https://github.com/staab/janet-schema
A schema building and validation library for Janet
https://github.com/staab/janet-schema
janet schema validation
Last synced: 6 months ago
JSON representation
A schema building and validation library for Janet
- Host: GitHub
- URL: https://github.com/staab/janet-schema
- Owner: staab
- License: mit
- Created: 2019-11-09T23:45:49.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2020-01-24T20:43:05.000Z (over 6 years ago)
- Last Synced: 2025-12-16T12:31:13.019Z (7 months ago)
- Topics: janet, schema, validation
- Size: 29.3 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# janet-schema
A schema library for Janet, inspired by plumatic/schema and jsonschema.
# Status
- [x] Basic type validation
- [x] Basic constraint validation
- [ ] Schema (meta) validation
- [ ] Data coercion based on given schema and coercer
# Usage
Import with:
```
(use schema)
```
## Basic
Create a schema:
```
(def schema {:stuff [{:a [:int]}]})
```
Check your data:
```
(pp (get-error schema {:stuff [{:a [1 2]} {:a [1 false 3]}]}))
# => {:code :type-error
:path (:stuff 1 :a 1)
:expected :int
:actual :boolean
:message "false is not a int"}
```
## Advanced
A schema is just a map with a `:t` property that's been registered as a type. `normalize` expands a schema into this format; literals are just a handy shorthand.
In addition to types, some extra modifiers can be added, depending on what type you're working with.
### Enum
Any schema can be annotated with `:enum`, which should be a tuple of allowed values.
```
(pp (get-error {:t :any :enum [1 :a "c"]} "d"))
# => {:message "\"d\" is not one of [1, :a, \"c\"]" ...}
```
### Maps
Map schemas can be annotated with `:required` properties, and `:closed`, which defaults to false.
```
(pp (get-error {:t :map :required [:a]} {}))
# => {:message "a is a required property" ...}
(pp (get-error {:t :map :closed true} {:a 1}))
# => {:message "a is not an allowed property" ...}
```
### Constraints
Constraints are additional rules that can be included on a schema which reference parts of the data and impose constraints.
Paths are the key part of constraints; in general they should be integer indexes or associative keys based on the data structure expected. The exception to this is when referencing all values in a dictionary, or a slice of some sequence. The syntax for this is `[:]` with optional indices before and after the colon in the case of indexed data types.
```
(pp
(get-error
{:t :map
:constraints [{:t :intersection
:search [:a "[1:]"]
:target [:b "[:]"]}]}
{:a [1 2 3] :b [1 2 3]}))
# => {:message "1 is not contained in the search path" ...}
```
## Iteration
If a single error is desired, `get-error` works great. If more than one error is desired, you can use `check-data`, which directly yields errors to the parent fiber. To control this, call it via a fiber, e.g.,
```
(loop [error :generate (fiber/new |(check-data schema data)))] ...)
```
## Extending
### Types
New types can be created using `define-type`, which takes a type keyword and the following options:
- `:type-is-valid` defines whether data is valid for the given type.
- `:iter-child-tuples` yields a tuple of `[child-schema child-data child-key]` tuples for recursive validation.
- `:iter-type-errors` yields any additional errors necessary for validating the type.
### Constraints
New constraints can be added by registering a method on the `iter-constraint-errors` multimethod, which dispatches on the constraint's `:t` property.
The registered function should take the desired constraint and the data corresponding to the place in the schema where the constraint was invoked. Constraint functions should yield any errors discovered.
# Disclaimer
This is pre-alpha software. Breaking changes are likely. Please open an issue if you'd like to use it in a project.