Leiningen coordinates:
[thi.ng/validate "0.1.0-SNAPSHOT"]
Validates coll (a map or vector) with given validation specs.
Returns a 2-element vector of the (possibly corrected) coll and a
map of errors (or nil, if there weren't any).
Specs have the following format:
key [validation-fn error-message correction-fn]
A key can be any datatype. If multiple validations should be applied to a key, then these must be given as a seq/vector:
key [[val-fn1 msg1] [val-fn2 msg2 corr-fn] ...]
Validation for a key stops with the first failure (so if val-fn1 fails
(and can't be corrected), val-fn2 will not be checked etc.)
For each spec only the validation-fn is required.
If an error-message is omitted, a generic one will be used.
The optional correction-fn takes a single arg (the current map value)
and should return a non-nil value as correction. If correction
succeeded, no error message will be added for that entry.
(v/validate {:a \"hello world\"}
:a (v/max-length 5 #(.substring % 0 5)))
; [{:a \"hello\"} nil]
Specs can also be given as nested maps, reflecting the structure of the collection:
key {:a {:b [validation-fn error-msg correction-fn]}
:c [validation-fn error-msg correction-fn]}
If a specs map contains the wildcard key :*, then that key's spec
is applied first to all keys in the data map at that parent path.
In the example below the wildcard spec ensures all items of :b are
positive numbers, then the last item of :b also needs to be > 50.
(v/validate {:a {:b [10 -20 30]}}
:a {:b {:* [(v/number) (v/pos)]
2 (v/greater-than 50)}})
; [{:a {:b [10 -20 30]}}
; {:a {:b {1 (\"must be positive\")
2 (\"must be greater than 50\"}}}]
The fail fast behavior also holds true for wildcard validation: If wildcard validation fails for a key, any additionally given validators for that key are ignored.
Some examples using various pre-defined validators:
(v/validate {:a {:name \"toxi\" :age 38}}
:a {:name [(v/string) (v/min-length 4)]
:age [(v/number) (v/less-than 35)]
:city [(v/required) (v/string)]})
; [{:a {:age 38, :name \"toxi\"}}
; {:a {:city (\"is required\"),
; :age (\"must be less than 35\")}}]
(v/validate {:aabb {:min [-100 -200 -300]
:max [100 200 300]}}
:aabb {:min {0 (v/neg) 1 (v/neg) 2 (v/neg)}
:max {:* (v/pos)}})
; [{:aabb {:max [100 200 300],
; :min [-100 -200 -300]}}
; nil]