aboutsummaryrefslogtreecommitdiff
path: root/lib/path/path.ml
blob: 7caba8752f126c71289b7706bc48b771fa616cff (plain)
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(** Describe the components in the path *)
type _ typ =
  | Int : int64 typ
  | String : string typ
  | Fixed : V_string.t -> unit typ

(** [get_param f] extract the value from the poositional argument.

    The function [f] call the server engine for the value *)
let get_param : type a. int -> (string -> string) -> a typ -> a =
 fun idx f -> function
  | Fixed _ -> ()
  | String -> f (string_of_int idx)
  | Int -> Int64.of_string (f (string_of_int idx))

let repr_param : type a. int -> a typ -> V_string.t =
 fun idx -> function
  | String | Int -> V_string.v (":" ^ string_of_int idx)
  | Fixed v -> v

let encode_param : type a. a -> a typ -> V_string.t =
 fun value -> function
  | String -> V_string.v value
  | Int -> V_string.v (Int64.to_string value)
  | Fixed v -> v

type _ t =
  | [] : unit t
  | ( :: ) : 'x t * 'y t -> ('x * 'y) t
  | T1 : 'a typ -> 'a t
  | T2 : 'a typ * 'b typ -> ('a * 'b) t
  | T3 : 'a typ * 'b typ * 'c typ -> ('a * 'b * 'c) t
  | T4 : 'a typ * 'b typ * 'c typ * 'd typ -> ('a * 'b * 'c * 'd) t

(** Count the number of arguments. *)
let rec count : type a. a t -> int = function
  | [] -> 0
  | T1 _ -> 1
  | T2 _ -> 2
  | T3 _ -> 3
  | T4 _ -> 4
  | p1 :: p2 -> count p1 + count p2

(** Extract the arguments from the path template *)
let unzip : 'a t -> (string -> string) -> 'a =
 fun t f ->
  let rec _unzip : type a. int -> a t -> a =
   fun idx path ->
    let extract idx p = get_param idx f p in
    match path with
    | [] -> ()
    | p1 :: tl -> (_unzip idx p1, _unzip (idx + count p1) tl)
    | T1 p1 -> extract idx p1
    | T2 (p1, p2) -> (extract idx p1, extract (idx + 1) p2)
    | T3 (p1, p2, p3) ->
        (extract idx p1, extract (idx + 1) p2, extract (idx + 2) p3)
    | T4 (p1, p2, p3, p4) ->
        ( extract idx p1,
          extract (idx + 1) p2,
          extract (idx + 2) p3,
          extract (idx + 3) p4 )
  in
  _unzip 0 t

let repr : 'a t -> V_string.t =
 fun t ->
  let rec _repr : type a. int -> V_string.t list -> a t -> V_string.t list =
   fun idx acc t ->
    match t with
    | [] -> acc
    | T1 t -> repr_param idx t :: acc
    | T2 (t1, t2) -> repr_param idx t1 :: repr_param (idx + 1) t2 :: acc
    | T3 (t1, t2, t3) ->
        repr_param idx t1
        :: repr_param (idx + 2) t2
        :: repr_param (idx + 3) t3
        :: acc
    | T4 (t1, t2, t3, t4) ->
        repr_param idx t1
        :: repr_param (idx + 1) t2
        :: repr_param (idx + 2) t3
        :: repr_param (idx + 3) t4
        :: acc
    | tx :: ty ->
        let idx' = count tx in
        let acc' = _repr idx' acc ty in
        _repr idx acc' tx
  in
  let args = _repr 0 [] t in
  V_string.concat ~sep:(V_string.v "/") args

let repr' a = repr a |> V_string.s

let build : 'a -> 'a t -> V_string.t =
 fun parameters uri ->
  ignore (parameters, uri);
  let rec _build : type a. V_string.t list -> a -> a t -> V_string.t list =
   fun acc value path ->
    match path with
    | [] -> acc
    | T1 t -> encode_param value t :: acc
    | T2 (t1, t2) ->
        let v1, v2 = value in
        encode_param v1 t1 :: encode_param v2 t2 :: acc
    | T3 (t1, t2, t3) ->
        let v1, v2, v3 = value in
        encode_param v1 t1 :: encode_param v2 t2 :: encode_param v3 t3 :: acc
    | T4 (t1, t2, t3, t4) ->
        let v1, v2, v3, v4 = value in
        encode_param v1 t1 :: encode_param v2 t2 :: encode_param v3 t3
        :: encode_param v4 t4 :: acc
    | tx :: ty ->
        let vx, vy = value in
        _build (_build acc vy ty) vx tx
  in
  V_string.concat ~sep:(V_string.v "/") (_build [] parameters uri)