@@ -10,6 +10,87 @@ open Learnocaml_data
10
10
11
11
let version = Learnocaml_version. v
12
12
13
+ module type COMPAT = sig
14
+ (* * List-based versions endowed with a lexicographic order. *)
15
+ type t
16
+
17
+ val to_string : t -> string
18
+
19
+ (* * Supported formats: [Compat.v "str"] where "str" is
20
+ either "n", "-n" (a signed integer), or "n.str".
21
+ However, [Compat.v "0.14.rc1"] or so is not supported for now. *)
22
+ val v : string -> t
23
+
24
+ (* * Note that trailing zeros are ignored, i.e. (v "1") and (v "1.0")
25
+ are equal compats. But (v "1") is higher than (v "1.-1"), itself
26
+ higher than (v "1.-2"), and so on. *)
27
+ val le : t -> t -> bool
28
+
29
+ val eq : t -> t -> bool
30
+
31
+ val lt : t -> t -> bool
32
+
33
+ type pred =
34
+ | Since of t | Upto of t | And of pred * pred
35
+
36
+ val compat : pred -> t -> bool
37
+ end
38
+
39
+ module Compat : COMPAT = struct
40
+
41
+ (* * List-based versions endowed with a lexicographic order. *)
42
+ type t = int list
43
+
44
+ let to_string = function
45
+ | [] -> failwith " Compat.to_string"
46
+ | n :: l ->
47
+ List. fold_left (fun r e -> r ^ " ." ^ string_of_int e) (string_of_int n) l
48
+
49
+ (* * Supported formats: [Compat.v "str"] where "str" is nonempty and
50
+ either "n", "-n" (a signed integer), or "n.str".
51
+ However, [Compat.v "0.14.rc1"] or so is not supported for now. *)
52
+ let v = function
53
+ | "" -> failwith " Compat.of_string"
54
+ | s -> String. split_on_char '.' s |> List. map int_of_string
55
+
56
+ (* * Note that trailing zeros are ignored, i.e. (v "1") and (v "1.0")
57
+ are equal versions. But (v "1") is higher than (v "1.-1"), itself
58
+ higher than (v "1.-2"), and so on. *)
59
+ let rec le v1 v2 = match v1, v2 with
60
+ | [] , [] -> true
61
+ | [] , 0 :: l2 -> le [] l2
62
+ | [] , n2 :: _ -> 0 < n2
63
+ | 0 :: l1 , [] -> le l1 []
64
+ | n1 :: _ , [] -> n1 < 0
65
+ | n1 :: l1 , n2 :: l2 -> n1 < n2 || (n1 = n2 && le l1 l2)
66
+
67
+ let eq v1 v2 = le v1 v2 && le v2 v1
68
+
69
+ let lt v1 v2 = not (le v2 v1)
70
+
71
+ type pred =
72
+ | Since of t (* * >= v0 *)
73
+ | Upto of t (* * < v1 *)
74
+ | And of pred * pred
75
+
76
+ let rec compat pred v =
77
+ match pred with
78
+ | Since v0 -> le v0 v
79
+ | Upto v1 -> lt v v1
80
+ | And (pred1 , pred2 ) -> compat pred1 v && compat pred2 v
81
+
82
+ end
83
+
84
+ (* Tests
85
+ assert Compat.(le (v "0.12") (v "0.13.0"));;
86
+ assert Compat.(le (v "0.13.0") (v "0.13.1"));;
87
+ assert Compat.(le (v "0.13.1") (v "0.14.0"));;
88
+ assert Compat.(le (v "0.14.0") (v "1.0.0"));;
89
+ assert Compat.(le (v "1.1.1") (v "1.1.1"));;
90
+ assert Compat.(le (v "0.2") (v "0.10"));;
91
+ assert Compat.(le (v "1.9.5") (v "1.10.0"));;
92
+ *)
93
+
13
94
type _ request =
14
95
| Static :
15
96
string list -> string request
@@ -124,6 +205,48 @@ type _ request =
124
205
| Invalid_request :
125
206
string -> string request
126
207
208
+ let supported_versions
209
+ : type resp . resp request -> Compat. pred
210
+ = function
211
+ | Static _
212
+ | Version _
213
+ | Nonce _
214
+ | Create_token (_, _, _)
215
+ | Create_teacher_token _
216
+ | Fetch_save _
217
+ | Archive_zip _
218
+ | Update_save (_, _)
219
+ | Git (_, _)
220
+ | Students_list _
221
+ | Set_students_list (_, _)
222
+ | Students_csv (_, _, _)
223
+ | Exercise_index _
224
+ | Exercise (_, _)
225
+ | Lesson_index _
226
+ | Lesson _
227
+ | Tutorial_index _
228
+ | Tutorial _
229
+ | Playground_index _
230
+ | Playground _
231
+ | Exercise_status_index _
232
+ | Exercise_status (_, _)
233
+ | Set_exercise_status (_, _)
234
+ | Partition (_, _, _, _)
235
+ | Invalid_request _ -> Compat. (Since (v " 0.12" ))
236
+
237
+ let is_supported
238
+ : type resp . ?current :Compat. t -> server :Compat. t -> resp request ->
239
+ (unit , string ) result =
240
+ fun ?(current = Compat. v Learnocaml_version. v) ~server request ->
241
+ let supp = supported_versions request in
242
+ if Compat. (compat (Since server) current) (* server <= current *)
243
+ && Compat. compat supp current (* request supported by current codebase *)
244
+ && Compat. compat supp server (* request supported by server *)
245
+ then Ok () else
246
+ Error (Printf. sprintf
247
+ {| API request not supported by server v.% s using client v.% s| }
248
+ (* NOTE: we may want to add some string_of_request call as well *)
249
+ (Compat. to_string server) (Compat. to_string current))
127
250
128
251
type http_request = {
129
252
meth : [ `GET | `POST of string ];
0 commit comments