Source file caqti_encoders.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(** Helper functions for creating Caqti types with custom encoder/decoders. *)

module Data = struct
  type _ t =
    | [] : unit t
    | ( :: ) : ('a * 'b t) -> ('a * 'b) t

  let rec make_value : type a. a t -> a =
    fun xs ->
    match xs with
    | [] -> ()
    | x :: xs -> x, make_value xs
  ;;
end

module Schema = struct
  type _ t =
    | [] : unit t
    | ( :: ) : ('a Caqti_type.t * 'b t) -> ('a * 'b) t

  let rec make_type : type a. a t -> a Caqti_type.t =
    fun xs ->
    match xs with
    | [] -> failwith "Schema shouldn't be empty"
    | x :: [] -> Caqti_type.(t2 x unit)
    | x :: xs -> Caqti_type.(t2 x (make_type xs))
  ;;
end

let custom
  : type a b.
    encode:(b -> (a Data.t, string) result)
    -> decode:(a -> (b, string) result)
    -> a Schema.t
    -> b Caqti_type.t
  =
  fun ~encode ~decode schema ->
  let typ = Schema.make_type schema in
  let encode data = encode data |> Result.map Data.make_value in
  Caqti_type.custom ~encode ~decode typ
;;

let custom_ok
  : type a b.
    encode:(b -> a Data.t) -> decode:(a -> b) -> a Schema.t -> b Caqti_type.t
  =
  let open CCFun in
  fun ~encode ~decode schema ->
    let typ = Schema.make_type schema in
    let encode = CCResult.(encode %> return %> map Data.make_value) in
    Caqti_type.custom ~encode ~decode:(decode %> CCResult.return) typ
;;