validate
A data/input validation library written in Ecko: composable validator functions, whole-object schemas, collect-all errors. Pure - no capabilities.
ecko get github.com/ecko-lang/validate
import validate
Pure computation: it declares no capabilities, so it cannot touch the network, the filesystem or the environment.
Version 0.9.5 - source - MIT.
check(data, schema)
check(data, schema) -> { field: [messages] }, empty when the data is valid.
Every rule of every field runs, so one call reports all the problems rather than the first one. A field absent from data is validated as null, which only required() objects to.
errors = validate.check(form, { email: [v.required(), v.email()] })
check_value(value, rules)
check_value(value, rules) -> [messages] for one value against one rule list.
The building block check uses per field. Useful on its own for a value that is not part of a map.
is_valid(data, schema)
is_valid(data, schema) -> whether data passes every rule.
The same work as check, when the messages are not wanted.
validate(data, schema)
validate(data, schema) -> data when valid, otherwise raises.
The raised error has kind "validation" and carries the same map check returns under errors, so a handler can render per-field messages.
try { user = validate.validate(body, schema) }
catch e { respond(422, e.errors) }
required()
required() -> fails on null. The only rule that does: every other validator passes a null value, so fields are optional unless you say otherwise.
optional()
optional() -> never fails. A marker for readers, since optional is already the default.
string()
string() -> fails unless the value is text.
int()
int() -> fails unless the value is a whole number.
float()
float() -> fails unless the value is a decimal number.
number()
number() -> fails unless the value is an int or a float.
bool()
bool() -> fails unless the value is true or false.
list()
list() -> fails unless the value is a list.
map()
map() -> fails unless the value is a map.
min(n)
min(n) -> fails when a number is below n. Non-numbers pass, so the type error is reported once by number() rather than by every rule.
max(n)
max(n) -> fails when a number is above n.
between(lo, hi)
between(lo, hi) -> fails when a number is outside lo..hi, inclusive.
positive()
positive() -> fails unless a number is greater than zero.
negative()
negative() -> fails unless a number is less than zero.
min_len(n)
min_len(n) -> fails when text is shorter than n characters.
max_len(n)
max_len(n) -> fails when text is longer than n characters.
len_between(lo, hi)
len_between(lo, hi) -> fails when text length is outside lo..hi.
pattern(re_pat, msg = null)
pattern(regex, msg?) -> fails when text does not match regex.
msg replaces the default "has an invalid format", which is worth setting - a regex in an error message helps nobody.
email()
email() -> fails unless the text looks like an email address.
A deliberately loose shape check (something@something.something). Whether an address exists is a question only sending to it can answer.
url()
url() -> fails unless the text is an http or https URL.
one_of(options)
one_of(options) -> fails unless the value is one of options.
Works for any type, and lists the allowed values in the message.
non_empty()
non_empty() -> fails on empty text or an empty list.
each(validator)
each(validator) -> applies validator to every element of a list.
Element failures are numbered from 1 and joined into one message, so the reader knows which item was wrong.
{ tags: [v.list(), v.each(v.min_len(2))] }
shape(schema)
shape(schema) -> validates a nested map against its own schema.
Sub-errors are flattened into a single field: message summary rather than a nested map. A deeper error tree is a future change.