Library
Module
Module type
Parameter
Class
Class type
Used to validate data described by type Yocaml.Data.t
to build validation pipelines. The aim of this module is to produce combinators for building validation pipelines that support nesting and that can transform any value described by the AST in Data
into arbitrary OCaml values.
type value_error =
| Invalid_shape of {
expected : string;
given : t;
}
| Invalid_list of {
errors : (int * value_error) Nel.t;
given : t list;
}
| Invalid_record of {
errors : record_error Nel.t;
given : (string * t) list;
}
| With_message of {
}
| Custom of custom_error
Type used to describe when a value does not have the expected form (for example when a float is given whereas a character string is expected). The With_message
is only parametrized by string in order to allow to write custom error messages.
and record_error =
| Missing_field of {
}
| Invalid_field of {
given : t;
field : string;
error : value_error;
}
Errors at the field level, for validating records.
type 'a validated_value = ('a, value_error) result
Used to validate data described by type t
to build validation pipelines.
type 'a validated_record = ('a, record_error Nel.t) result
Used to validate records described by type t
to build validation pipelines.
Helpers for creating errors.
val fail_with : given:string -> string -> 'a validated_value
fail_with ~given message
returns a validation error associated to a message.
val fail_with_custom : custom_error -> 'a validated_value
fail_with_custom err
returns a custonm validation error.
Validators act on classic AST values. They are used to validate the fields of a record (or the inhabitants of a list). They are often used as arguments to the optional
, optional_or
and required
functions.
val null : t -> unit validated_value
ensure that a value is null
.
val bool : t -> bool validated_value
ensure that a value is a boolean.
val int : t -> int validated_value
ensure that a value is an int (or a float).
val float : t -> float validated_value
ensure that a value is an float.
val string : ?strict:bool -> t -> string validated_value
ensure that a value is a string, if strict
is false
it will consider bool
, int
, float
also as strings.
val list_of : (t -> 'a validated_value) -> t -> 'a list validated_value
list_of v x
ensure that x
is a list that satisfy v
(all errors are collected).
val record :
((string * t) list -> 'a validated_record) ->
t ->
'a validated_value
record v x
ensure that x
has the shape validated by v
(all errors are collected).
val option : (t -> 'a validated_value) -> t -> 'a option validated_value
option v x
validate a value using v
that can be null
wrapped into an option.
val pair :
(t -> 'a validated_value) ->
(t -> 'b validated_value) ->
t ->
('a * 'b) validated_value
pair fst snd x
validated a pair (described as Yocaml.Data.pair
, {"fst": a, "snd": b}
).
val triple :
(t -> 'a validated_value) ->
(t -> 'b validated_value) ->
(t -> 'c validated_value) ->
t ->
('a * 'b * 'c) validated_value
triple f g h v
define a triple validator built on top of pair
. (Under the hood, it treat value like that (x, (y, z))
. )
val quad :
(t -> 'a validated_value) ->
(t -> 'b validated_value) ->
(t -> 'c validated_value) ->
(t -> 'd validated_value) ->
t ->
('a * 'b * 'c * 'd) validated_value
quad f g h i v
define a quad validator built on top of triple
. (Under the hood, it treat value like that (w, (x, (y, z)))
. )
val sum : (string * (t -> 'a validated_value)) list -> t -> 'a validated_value
sum [(k, v)] value
validated a sum value using the representation described in Yocaml.Data.sum
: {"constr": const_value, "value": e}
.
val either :
(t -> 'a validated_value) ->
(t -> 'b validated_value) ->
t ->
('a, 'b) Either.t validated_value
either left right v
validated a either
value.
Validators to use when data is already parsed.
val positive : int -> int validated_value
positive x
ensure that x
is positive.
val positive' : float -> float validated_value
positive x
ensure that x
is positive.
val bounded : min:int -> max:int -> int -> int validated_value
bounded ~min ~max x
ensure that x
is included in the range [min;max]
(both included).
val bounded' : min:float -> max:float -> float -> float validated_value
bounded ~min ~max x
ensure that x
is included in the range [min;max]
(both included).
val non_empty : 'a list -> 'a list validated_value
non_empty l
ensure that l
is non-empty.
val equal :
?pp:(Format.formatter -> 'a -> unit) ->
?equal:('a -> 'a -> bool) ->
'a ->
'a ->
'a validated_value
equal ?pp ?equal x y
ensure that y
is equal to x
. pp
is used for error-reporting.
val not_equal :
?pp:(Format.formatter -> 'a -> unit) ->
?equal:('a -> 'a -> bool) ->
'a ->
'a ->
'a validated_value
not_equal ?pp ?equal x y
ensure that y
is different of x
. pp
is used for error-reporting.
val gt :
?pp:(Format.formatter -> 'a -> unit) ->
?compare:('a -> 'a -> int) ->
'a ->
'a ->
'a validated_value
gt ?pp ?equal x y
ensure that x
is greater than y
. pp
is used for error-reporting.
val ge :
?pp:(Format.formatter -> 'a -> unit) ->
?compare:('a -> 'a -> int) ->
'a ->
'a ->
'a validated_value
ge ?pp ?equal x y
ensure that x
is greater or equal to y
. pp
is used for error-reporting.
val lt :
?pp:(Format.formatter -> 'a -> unit) ->
?compare:('a -> 'a -> int) ->
'a ->
'a ->
'a validated_value
lt ?pp ?equal x y
ensure that x
is lesser than y
. pp
is used for error-reporting.
val le :
?pp:(Format.formatter -> 'a -> unit) ->
?compare:('a -> 'a -> int) ->
'a ->
'a ->
'a validated_value
le ?pp ?equal x y
ensure that x
is lesser or equal to y
. pp
is used for error-reporting.
val one_of :
?pp:(Format.formatter -> 'a -> unit) ->
?equal:('a -> 'a -> bool) ->
'a list ->
'a ->
'a validated_value
one_of ?pp ?equal li x
ensure that x
is include in li
. pp
is used for error-reporting.
val where :
?pp:(Format.formatter -> 'a -> unit) ->
?message:('a -> string) ->
('a -> bool) ->
'a ->
'a validated_value
where ?pp predicate x
ensure that x
is satisfying predicate
. pp
is used for error-reporting.
val const : 'a -> 'b -> ('a, 'c) result
const k r
wrap k
as valid and discard r
.
module Infix : sig ... end
Infix operators are essentially used to compose data validators (unlike binding operators, which are used to compose record validation fragments).
val (&) :
('a -> 'b validated_value) ->
('b -> 'c validated_value) ->
'a ->
'c validated_value
(v1 & v2) x
sequentially compose v2 (v1 x)
, so v1
following by v2
. For example : int &> positive &> c
.
val (/) :
('a -> 'b validated_value) ->
('a -> 'b validated_value) ->
'a ->
'b validated_value
(v1 / v2) x
perform v1 x
and if it fail, performs v2 x
.
val ($) : ('a -> 'b validated_value) -> ('b -> 'c) -> 'a -> 'c validated_value
(v1 $ f) x
perform f
on the result of v1 x
.
Field validators are used to describe parallel validation strategies for each field in a record
and collect errors by field. Usually, a field validator takes as arguments the associative list of keys/values in a record, the name of the field to be observed and a regular validator.
val required :
(string * t) list ->
string ->
(t -> 'a validated_value) ->
'a validated_record
required assoc field validator
required field
of assoc
, validated by validator
.
val optional :
(string * t) list ->
string ->
(t -> 'a validated_value) ->
'a option validated_record
optional assoc field validator
optional field
of assoc
, validated by validator
.
val optional_or :
(string * t) list ->
string ->
default:'a ->
(t -> 'a validated_value) ->
'a validated_record
optional_or ~default assoc field validator
optional field
of assoc
, validated by validator
. If the field does not exists, it return default. (default
is not validated)
module Syntax : sig ... end
Binding operators are used to link fields together to build a complete validation.
let+ x = v in k x
is map (fun x -> k x) v
.
val and+ :
'a validated_record ->
'b validated_record ->
('a * 'b) validated_record
let+ x = v and+ y = w in k x y
is map2 (fun x y -> k x y) v w
.
let* r = f x in return r
tries to produce a result Ok
from the expression f x
, if the expression returns Error _
, the computation chain is interrupted.
Warning: the semantics of let*
are significantly different from a succession of let+ ... and+ ...
which allow errors to be collected in parallel (independently), whereas let*
captures them sequentially. The composition of let*
and let+
is tricky and let*
should only be used to validate preconditions.