objective categorical abstract machine language personal data server
1(* parse lexicon json files into lexicon_types *)
2
3open Lexicon_types
4
5let get_string_opt key json =
6 match json with
7 | `Assoc pairs -> (
8 match List.assoc_opt key pairs with Some (`String s) -> Some s | _ -> None )
9 | _ ->
10 None
11
12let get_string key json =
13 match get_string_opt key json with
14 | Some s ->
15 s
16 | None ->
17 failwith ("missing required string field: " ^ key)
18
19let get_int_opt key json =
20 match json with
21 | `Assoc pairs -> (
22 match List.assoc_opt key pairs with Some (`Int i) -> Some i | _ -> None )
23 | _ ->
24 None
25
26let get_int key json =
27 match get_int_opt key json with
28 | Some i ->
29 i
30 | None ->
31 failwith ("missing required int field: " ^ key)
32
33let get_bool_opt key json =
34 match json with
35 | `Assoc pairs -> (
36 match List.assoc_opt key pairs with Some (`Bool b) -> Some b | _ -> None )
37 | _ ->
38 None
39
40let get_list_opt key json =
41 match json with
42 | `Assoc pairs -> (
43 match List.assoc_opt key pairs with Some (`List l) -> Some l | _ -> None )
44 | _ ->
45 None
46
47let get_string_list_opt key json =
48 match get_list_opt key json with
49 | Some l ->
50 Some (List.filter_map (function `String s -> Some s | _ -> None) l)
51 | None ->
52 None
53
54let get_int_list_opt key json =
55 match get_list_opt key json with
56 | Some l ->
57 Some (List.filter_map (function `Int i -> Some i | _ -> None) l)
58 | None ->
59 None
60
61let get_assoc key json =
62 match json with
63 | `Assoc pairs -> (
64 match List.assoc_opt key pairs with
65 | Some (`Assoc _ as a) ->
66 Some a
67 | _ ->
68 None )
69 | _ ->
70 None
71
72(* parse type definition from json *)
73let rec parse_type_def json : type_def =
74 let type_str = get_string "type" json in
75 match type_str with
76 | "string" ->
77 String
78 { format= get_string_opt "format" json
79 ; min_length= get_int_opt "minLength" json
80 ; max_length= get_int_opt "maxLength" json
81 ; min_graphemes= get_int_opt "minGraphemes" json
82 ; max_graphemes= get_int_opt "maxGraphemes" json
83 ; known_values= get_string_list_opt "knownValues" json
84 ; enum= get_string_list_opt "enum" json
85 ; const= get_string_opt "const" json
86 ; default= get_string_opt "default" json
87 ; description= get_string_opt "description" json }
88 | "integer" ->
89 Integer
90 { minimum= get_int_opt "minimum" json
91 ; maximum= get_int_opt "maximum" json
92 ; enum= get_int_list_opt "enum" json
93 ; const= get_int_opt "const" json
94 ; default= get_int_opt "default" json
95 ; description= get_string_opt "description" json }
96 | "boolean" ->
97 Boolean
98 { const= get_bool_opt "const" json
99 ; default= get_bool_opt "default" json
100 ; description= get_string_opt "description" json }
101 | "bytes" ->
102 Bytes
103 { min_length= get_int_opt "minLength" json
104 ; max_length= get_int_opt "maxLength" json
105 ; description= get_string_opt "description" json }
106 | "blob" ->
107 Blob
108 { accept= get_string_list_opt "accept" json
109 ; max_size= get_int_opt "maxSize" json
110 ; description= get_string_opt "description" json }
111 | "cid-link" ->
112 CidLink {description= get_string_opt "description" json}
113 | "array" ->
114 let items_json =
115 match get_assoc "items" json with
116 | Some j ->
117 j
118 | None ->
119 failwith "array type missing items"
120 in
121 Array
122 { items= parse_type_def items_json
123 ; min_length= get_int_opt "minLength" json
124 ; max_length= get_int_opt "maxLength" json
125 ; description= get_string_opt "description" json }
126 | "object" ->
127 Object (parse_object_spec json)
128 | "ref" ->
129 Ref
130 { ref_= get_string "ref" json
131 ; description= get_string_opt "description" json }
132 | "union" ->
133 Union
134 { refs=
135 ( match get_string_list_opt "refs" json with
136 | Some l ->
137 l
138 | None ->
139 [] )
140 ; closed= get_bool_opt "closed" json
141 ; description= get_string_opt "description" json }
142 | "token" ->
143 Token {description= get_string_opt "description" json}
144 | "unknown" ->
145 Unknown {description= get_string_opt "description" json}
146 | "query" ->
147 Query (parse_query_spec json)
148 | "procedure" ->
149 Procedure (parse_procedure_spec json)
150 | "subscription" ->
151 Subscription (parse_subscription_spec json)
152 | "record" ->
153 Record (parse_record_spec json)
154 | t ->
155 failwith ("unknown type: " ^ t)
156
157and parse_object_spec json : object_spec =
158 let properties =
159 match get_assoc "properties" json with
160 | Some (`Assoc pairs) ->
161 List.map
162 (fun (name, prop_json) ->
163 let type_def = parse_type_def prop_json in
164 let description = get_string_opt "description" prop_json in
165 (name, {type_def; description}) )
166 pairs
167 | _ ->
168 []
169 in
170 { properties
171 ; required= get_string_list_opt "required" json
172 ; nullable= get_string_list_opt "nullable" json
173 ; description= get_string_opt "description" json }
174
175and parse_params_spec json : params_spec =
176 let properties =
177 match get_assoc "properties" json with
178 | Some (`Assoc pairs) ->
179 List.map
180 (fun (name, prop_json) ->
181 let type_def = parse_type_def prop_json in
182 let description = get_string_opt "description" prop_json in
183 (name, {type_def; description}) )
184 pairs
185 | _ ->
186 []
187 in
188 { properties
189 ; required= get_string_list_opt "required" json
190 ; description= get_string_opt "description" json }
191
192and parse_body_def json : body_def =
193 { encoding= get_string "encoding" json
194 ; schema=
195 ( match get_assoc "schema" json with
196 | Some j ->
197 Some (parse_type_def j)
198 | None ->
199 None )
200 ; description= get_string_opt "description" json }
201
202and parse_error_def json : error_def =
203 {name= get_string "name" json; description= get_string_opt "description" json}
204
205and parse_query_spec json : query_spec =
206 let parameters =
207 match get_assoc "parameters" json with
208 | Some j ->
209 Some (parse_params_spec j)
210 | None ->
211 None
212 in
213 let output =
214 match get_assoc "output" json with
215 | Some j ->
216 Some (parse_body_def j)
217 | None ->
218 None
219 in
220 let errors =
221 match get_list_opt "errors" json with
222 | Some l ->
223 Some
224 (List.map
225 (function
226 | `Assoc _ as j ->
227 parse_error_def j
228 | _ ->
229 failwith "invalid error def" )
230 l )
231 | None ->
232 None
233 in
234 {parameters; output; errors; description= get_string_opt "description" json}
235
236and parse_procedure_spec json : procedure_spec =
237 let parameters =
238 match get_assoc "parameters" json with
239 | Some j ->
240 Some (parse_params_spec j)
241 | None ->
242 None
243 in
244 let input =
245 match get_assoc "input" json with
246 | Some j ->
247 Some (parse_body_def j)
248 | None ->
249 None
250 in
251 let output =
252 match get_assoc "output" json with
253 | Some j ->
254 Some (parse_body_def j)
255 | None ->
256 None
257 in
258 let errors =
259 match get_list_opt "errors" json with
260 | Some l ->
261 Some
262 (List.map
263 (function
264 | `Assoc _ as j ->
265 parse_error_def j
266 | _ ->
267 failwith "invalid error def" )
268 l )
269 | None ->
270 None
271 in
272 { parameters
273 ; input
274 ; output
275 ; errors
276 ; description= get_string_opt "description" json }
277
278and parse_subscription_spec json : subscription_spec =
279 let parameters =
280 match get_assoc "parameters" json with
281 | Some j ->
282 Some (parse_params_spec j)
283 | None ->
284 None
285 in
286 let message =
287 match get_assoc "message" json with
288 | Some j ->
289 Some (parse_body_def j)
290 | None ->
291 None
292 in
293 let errors =
294 match get_list_opt "errors" json with
295 | Some l ->
296 Some
297 (List.map
298 (function
299 | `Assoc _ as j ->
300 parse_error_def j
301 | _ ->
302 failwith "invalid error def" )
303 l )
304 | None ->
305 None
306 in
307 {parameters; message; errors; description= get_string_opt "description" json}
308
309and parse_record_spec json : record_spec =
310 let key = get_string "key" json in
311 let record_json =
312 match get_assoc "record" json with
313 | Some j ->
314 j
315 | None ->
316 failwith "record type missing record field"
317 in
318 { key
319 ; record= parse_object_spec record_json
320 ; description= get_string_opt "description" json }
321
322(* parse complete lexicon document *)
323let parse_lexicon_doc json : lexicon_doc =
324 let lexicon = get_int "lexicon" json in
325 let id = get_string "id" json in
326 let revision = get_int_opt "revision" json in
327 let description = get_string_opt "description" json in
328 let defs =
329 match get_assoc "defs" json with
330 | Some (`Assoc pairs) ->
331 List.map
332 (fun (name, def_json) -> {name; type_def= parse_type_def def_json})
333 pairs
334 | _ ->
335 []
336 in
337 {lexicon; id; revision; description; defs}
338
339(* parse lexicon file *)
340let parse_file path : parse_result =
341 try
342 let json = Yojson.Safe.from_file path in
343 Ok (parse_lexicon_doc json)
344 with
345 | Yojson.Json_error e ->
346 Error ("JSON parse error: " ^ e)
347 | Failure e ->
348 Error ("Parse error: " ^ e)
349 | e ->
350 Error ("Unexpected error: " ^ Printexc.to_string e)
351
352(* parse json string *)
353let parse_string content : parse_result =
354 try
355 let json = Yojson.Safe.from_string content in
356 Ok (parse_lexicon_doc json)
357 with
358 | Yojson.Json_error e ->
359 Error ("JSON parse error: " ^ e)
360 | Failure e ->
361 Error ("Parse error: " ^ e)
362 | e ->
363 Error ("Unexpected error: " ^ Printexc.to_string e)