···17171818let error_to_string e = Format.asprintf "%a" pp_error e
19192020+type json = Simdjsont.Json.t
2121+2222+let int_of_int64 (i : int64) : int option =
2323+ if
2424+ Int64.compare i (Int64.of_int max_int) > 0
2525+ || Int64.compare i (Int64.of_int min_int) < 0
2626+ then None
2727+ else Some (Int64.to_int i)
2828+2029(** Helper to get string from JSON *)
2130let get_string key json =
2231 match json with
2323- | `Assoc pairs -> (
3232+ | Simdjsont.Json.Object pairs -> (
2433 match List.assoc_opt key pairs with
2525- | Some (`String s) -> Some s
3434+ | Some (Simdjsont.Json.String s) -> Some s
2635 | _ -> None)
2736 | _ -> None
2837···3241(** Helper to get int from JSON *)
3342let get_int key json =
3443 match json with
3535- | `Assoc pairs -> (
3636- match List.assoc_opt key pairs with Some (`Int i) -> Some i | _ -> None)
4444+ | Simdjsont.Json.Object pairs -> (
4545+ match List.assoc_opt key pairs with
4646+ | Some (Simdjsont.Json.Int i) -> int_of_int64 i
4747+ | _ -> None)
3748 | _ -> None
38493950(** Helper to get bool from JSON *)
4051let get_bool key json =
4152 match json with
4242- | `Assoc pairs -> (
4343- match List.assoc_opt key pairs with Some (`Bool b) -> Some b | _ -> None)
5353+ | Simdjsont.Json.Object pairs -> (
5454+ match List.assoc_opt key pairs with
5555+ | Some (Simdjsont.Json.Bool b) -> Some b
5656+ | _ -> None)
4457 | _ -> None
45584659(** Helper to get list from JSON *)
4760let get_list key json =
4861 match json with
4949- | `Assoc pairs -> (
5050- match List.assoc_opt key pairs with Some (`List l) -> Some l | _ -> None)
6262+ | Simdjsont.Json.Object pairs -> (
6363+ match List.assoc_opt key pairs with
6464+ | Some (Simdjsont.Json.Array l) -> Some l
6565+ | _ -> None)
5166 | _ -> None
52675368(** Helper to get assoc from JSON *)
5469let get_assoc key json =
5570 match json with
5656- | `Assoc pairs -> (
7171+ | Simdjsont.Json.Object pairs -> (
5772 match List.assoc_opt key pairs with
5858- | Some (`Assoc a) -> Some a
7373+ | Some (Simdjsont.Json.Object a) -> Some a
5974 | _ -> None)
6075 | _ -> None
6176···6378let get_string_list key json =
6479 match get_list key json with
6580 | Some l ->
6666- Some (List.filter_map (function `String s -> Some s | _ -> None) l)
8181+ Some
8282+ (List.filter_map
8383+ (function Simdjsont.Json.String s -> Some s | _ -> None)
8484+ l)
6785 | None -> None
68866987(** Helper to get int list *)
7088let get_int_list key json =
7189 match get_list key json with
7272- | Some l -> Some (List.filter_map (function `Int i -> Some i | _ -> None) l)
9090+ | Some l ->
9191+ Some
9292+ (List.filter_map
9393+ (function Simdjsont.Json.Int i -> int_of_int64 i | _ -> None)
9494+ l)
7395 | None -> None
74967597(** Parse a field type from JSON *)
···149171 })
150172 | "array" -> (
151173 match json with
152152- | `Assoc pairs -> (
174174+ | Simdjsont.Json.Object pairs -> (
153175 match List.assoc_opt "items" pairs with
154176 | Some items_json -> (
155177 match parse_field_type items_json with
···263285 | Some encoding ->
264286 let schema =
265287 match json with
266266- | `Assoc pairs -> (
288288+ | Simdjsont.Json.Object pairs -> (
267289 match List.assoc_opt "schema" pairs with
268290 | Some schema_json -> (
269291 match parse_field_type schema_json with
···292314(** Parse message from JSON *)
293315let parse_message json : (Schema.message, error) result =
294316 match json with
295295- | `Assoc pairs -> (
317317+ | Simdjsont.Json.Object pairs -> (
296318 match List.assoc_opt "schema" pairs with
297319 | Some schema_json -> (
298320 match parse_field_type schema_json with
···333355 | None -> Schema.Any
334356 in
335357 match json with
336336- | `Assoc pairs -> (
358358+ | Simdjsont.Json.Object pairs -> (
337359 match List.assoc_opt "record" pairs with
338360 | Some record_json -> (
339361 match parse_object_type record_json with
···352374 | "query" ->
353375 let parameters =
354376 match json with
355355- | `Assoc pairs -> (
377377+ | Simdjsont.Json.Object pairs -> (
356378 match List.assoc_opt "parameters" pairs with
357379 | Some p -> (
358380 match parse_params p with
···363385 in
364386 let output =
365387 match json with
366366- | `Assoc pairs -> (
388388+ | Simdjsont.Json.Object pairs -> (
367389 match List.assoc_opt "output" pairs with
368390 | Some o -> (
369391 match parse_body o with
···383405 | "procedure" ->
384406 let parameters =
385407 match json with
386386- | `Assoc pairs -> (
408408+ | Simdjsont.Json.Object pairs -> (
387409 match List.assoc_opt "parameters" pairs with
388410 | Some p -> (
389411 match parse_params p with
···394416 in
395417 let input =
396418 match json with
397397- | `Assoc pairs -> (
419419+ | Simdjsont.Json.Object pairs -> (
398420 match List.assoc_opt "input" pairs with
399421 | Some i -> (
400422 match parse_body i with
···405427 in
406428 let output =
407429 match json with
408408- | `Assoc pairs -> (
430430+ | Simdjsont.Json.Object pairs -> (
409431 match List.assoc_opt "output" pairs with
410432 | Some o -> (
411433 match parse_body o with
···426448 | "subscription" ->
427449 let parameters =
428450 match json with
429429- | `Assoc pairs -> (
451451+ | Simdjsont.Json.Object pairs -> (
430452 match List.assoc_opt "parameters" pairs with
431453 | Some p -> (
432454 match parse_params p with
···437459 in
438460 let message =
439461 match json with
440440- | `Assoc pairs -> (
462462+ | Simdjsont.Json.Object pairs -> (
441463 match List.assoc_opt "message" pairs with
442464 | Some m -> (
443465 match parse_message m with
···565587(** Parse a complete lexicon from JSON *)
566588let parse_lexicon json : (Schema.lexicon, error) result =
567589 match json with
568568- | `Assoc _ -> (
590590+ | Simdjsont.Json.Object _ -> (
569591 match get_int "lexicon" json with
570592 | None -> Error (`Missing_field "lexicon")
571593 | Some version -> (
···593615594616(** Parse a lexicon from a JSON string *)
595617let of_string s : (Schema.lexicon, error) result =
596596- try
597597- let json = Yojson.Basic.from_string s in
598598- parse_lexicon json
599599- with Yojson.Json_error msg -> Error (`Parse_error msg)
618618+ match Simdjsont.decode Simdjsont.Codec.value s with
619619+ | Ok json -> parse_lexicon json
620620+ | Error msg -> Error (`Parse_error msg)
600621601622(** Parse a lexicon from a file *)
602623let of_file path : (Schema.lexicon, error) result =
603624 try
604604- let json = Yojson.Basic.from_file path in
605605- parse_lexicon json
606606- with
607607- | Yojson.Json_error msg -> Error (`Parse_error msg)
608608- | Sys_error msg -> Error (`Parse_error msg)
625625+ let ic = open_in path in
626626+ let len = in_channel_length ic in
627627+ let s = really_input_string ic len in
628628+ close_in ic;
629629+ of_string s
630630+ with Sys_error msg -> Error (`Parse_error msg)
+84-51
lib/lexicon/validator.ml
···202202203203(* === JSON value helpers === *)
204204205205-type json = Yojson.Basic.t
205205+type json = Simdjsont.Json.t
206206207207let get_string key = function
208208- | `Assoc pairs -> (
208208+ | Simdjsont.Json.Object pairs -> (
209209 match List.assoc_opt key pairs with
210210- | Some (`String s) -> Some s
210210+ | Some (Simdjsont.Json.String s) -> Some s
211211 | _ -> None)
212212 | _ -> None
213213214214let get_int key = function
215215- | `Assoc pairs -> (
216216- match List.assoc_opt key pairs with Some (`Int i) -> Some i | _ -> None)
215215+ | Simdjsont.Json.Object pairs -> (
216216+ match List.assoc_opt key pairs with
217217+ | Some (Simdjsont.Json.Int i) ->
218218+ if
219219+ Int64.compare i (Int64.of_int max_int) > 0
220220+ || Int64.compare i (Int64.of_int min_int) < 0
221221+ then None
222222+ else Some (Int64.to_int i)
223223+ | _ -> None)
217224 | _ -> None
218225219226let get_bool key = function
220220- | `Assoc pairs -> (
221221- match List.assoc_opt key pairs with Some (`Bool b) -> Some b | _ -> None)
227227+ | Simdjsont.Json.Object pairs -> (
228228+ match List.assoc_opt key pairs with
229229+ | Some (Simdjsont.Json.Bool b) -> Some b
230230+ | _ -> None)
222231 | _ -> None
223232224224-let is_null = function `Null -> true | _ -> false
233233+let is_null = function Simdjsont.Json.Null -> true | _ -> false
225234226235(* === Field validators === *)
227236228237(** Validate a boolean value *)
229238let validate_boolean ~path json =
230230- match json with `Bool _ -> [] | _ -> [ error ~path "expected boolean" ]
239239+ match json with
240240+ | Simdjsont.Json.Bool _ -> []
241241+ | _ -> [ error ~path "expected boolean" ]
231242232243(** Validate an integer value with constraints *)
233244let validate_integer ~path ?minimum ?maximum ?enum ?const json =
234245 match json with
235235- | `Int i ->
236236- let errs = ref [] in
237237- (match const with
238238- | Some c when i <> c ->
239239- errs := error ~path (Printf.sprintf "must be %d" c) :: !errs
240240- | _ -> ());
241241- (match enum with
242242- | Some values when not (List.mem i values) ->
243243- errs := error ~path "value not in enum" :: !errs
244244- | _ -> ());
245245- (match minimum with
246246- | Some min when i < min ->
247247- errs := error ~path (Printf.sprintf "must be >= %d" min) :: !errs
248248- | _ -> ());
249249- (match maximum with
250250- | Some max when i > max ->
251251- errs := error ~path (Printf.sprintf "must be <= %d" max) :: !errs
252252- | _ -> ());
253253- !errs
246246+ | Simdjsont.Json.Int i64 -> (
247247+ let i_opt =
248248+ if
249249+ Int64.compare i64 (Int64.of_int max_int) > 0
250250+ || Int64.compare i64 (Int64.of_int min_int) < 0
251251+ then None
252252+ else Some (Int64.to_int i64)
253253+ in
254254+ match i_opt with
255255+ | None -> [ error ~path "integer out of int range" ]
256256+ | Some i ->
257257+ let errs = ref [] in
258258+ (match const with
259259+ | Some c when i <> c ->
260260+ errs := error ~path (Printf.sprintf "must be %d" c) :: !errs
261261+ | _ -> ());
262262+ (match enum with
263263+ | Some values when not (List.mem i values) ->
264264+ errs := error ~path "value not in enum" :: !errs
265265+ | _ -> ());
266266+ (match minimum with
267267+ | Some min when i < min ->
268268+ errs := error ~path (Printf.sprintf "must be >= %d" min) :: !errs
269269+ | _ -> ());
270270+ (match maximum with
271271+ | Some max when i > max ->
272272+ errs := error ~path (Printf.sprintf "must be <= %d" max) :: !errs
273273+ | _ -> ());
274274+ !errs)
254275 | _ -> [ error ~path "expected integer" ]
255276256277(** Validate a string value with constraints *)
257278let validate_string ~path ?format ?min_length ?max_length ?min_graphemes
258279 ?max_graphemes ?enum ?const ?known_values:_ json =
259280 match json with
260260- | `String s ->
281281+ | Simdjsont.Json.String s ->
261282 let errs = ref [] in
262283 (match const with
263284 | Some c when s <> c ->
···301322(** Validate a bytes value (expects $bytes object) *)
302323let validate_bytes ~path ?min_length ?max_length json =
303324 match json with
304304- | `Assoc pairs -> (
325325+ | Simdjsont.Json.Object pairs -> (
305326 match List.assoc_opt "$bytes" pairs with
306306- | Some (`String b64) ->
327327+ | Some (Simdjsont.Json.String b64) ->
307328 (* Decode base64 to get actual length *)
308329 let len = String.length b64 * 3 / 4 in
309330 (* approximate *)
···327348(** Validate a CID link (expects $link object) *)
328349let validate_cid_link ~path json =
329350 match json with
330330- | `Assoc pairs -> (
351351+ | Simdjsont.Json.Object pairs -> (
331352 match List.assoc_opt "$link" pairs with
332332- | Some (`String _cid) -> []
353353+ | Some (Simdjsont.Json.String _cid) -> []
333354 | _ -> [ error ~path "expected $link object" ])
334355 | _ -> [ error ~path "expected $link object" ]
335356336357(** Validate a blob value *)
337358let validate_blob ~path ?max_size ?accept json =
338359 match json with
339339- | `Assoc pairs -> (
360360+ | Simdjsont.Json.Object pairs -> (
340361 match List.assoc_opt "$type" pairs with
341341- | Some (`String "blob") ->
362362+ | Some (Simdjsont.Json.String "blob") ->
342363 let errs = ref [] in
343364 (* Check mimeType *)
344365 (match List.assoc_opt "mimeType" pairs with
345345- | Some (`String mime) -> (
366366+ | Some (Simdjsont.Json.String mime) -> (
346367 match accept with
347368 | Some patterns ->
348369 let matches =
···365386 | None -> errs := error ~path "missing mimeType" :: !errs);
366387 (* Check size *)
367388 (match List.assoc_opt "size" pairs with
368368- | Some (`Int size) -> (
369369- match max_size with
370370- | Some max when size > max ->
371371- errs :=
372372- error ~path (Printf.sprintf "blob size must be <= %d" max)
373373- :: !errs
374374- | _ -> ())
389389+ | Some (Simdjsont.Json.Int size64) -> (
390390+ let size =
391391+ if
392392+ Int64.compare size64 (Int64.of_int max_int) > 0
393393+ || Int64.compare size64 (Int64.of_int min_int) < 0
394394+ then None
395395+ else Some (Int64.to_int size64)
396396+ in
397397+ match size with
398398+ | None -> errs := error ~path "size out of int range" :: !errs
399399+ | Some size -> (
400400+ match max_size with
401401+ | Some max when size > max ->
402402+ errs :=
403403+ error ~path
404404+ (Printf.sprintf "blob size must be <= %d" max)
405405+ :: !errs
406406+ | _ -> ()))
375407 | Some _ -> errs := error ~path "size must be integer" :: !errs
376408 | None -> errs := error ~path "missing size" :: !errs);
377409 (* Check ref *)
···393425 This is part of the data model restrictions. *)
394426let validate_unknown ~path json =
395427 match json with
396396- | `Bool _ -> [ error ~path "unknown type cannot contain boolean" ]
397397- | `Assoc pairs -> (
428428+ | Simdjsont.Json.Bool _ ->
429429+ [ error ~path "unknown type cannot contain boolean" ]
430430+ | Simdjsont.Json.Object pairs -> (
398431 (* Check for $bytes - not allowed in unknown *)
399432 match List.assoc_opt "$bytes" pairs with
400433 | Some _ -> [ error ~path "unknown type cannot contain bytes ($bytes)" ]
401434 | None -> (
402435 (* Check for blob ($type: "blob") - not allowed in unknown *)
403436 match List.assoc_opt "$type" pairs with
404404- | Some (`String "blob") ->
437437+ | Some (Simdjsont.Json.String "blob") ->
405438 [ error ~path "unknown type cannot contain blob" ]
406439 | _ -> []))
407440 | _ -> []
···455488456489and validate_array ~resolver ~path (arr : Schema.array_type) json =
457490 match json with
458458- | `List items ->
491491+ | Simdjsont.Json.Array items ->
459492 let errs = ref [] in
460493 (* Check length constraints *)
461494 let len = List.length items in
···484517485518and validate_object ~resolver ~path (obj : Schema.object_type) json =
486519 match json with
487487- | `Assoc pairs ->
520520+ | Simdjsont.Json.Object pairs ->
488521 let errs = ref [] in
489522 (* Check required fields *)
490523 List.iter
···520553 | None -> (
521554 (* Fallback: require an object for unresolved refs *)
522555 match json with
523523- | `Assoc _ -> []
556556+ | Simdjsont.Json.Object _ -> []
524557 | _ -> [ error ~path "expected object for ref" ])
525558526559and validate_union ~resolver ~path (union : Schema.union_type) json =
527560 match json with
528528- | `Assoc pairs -> (
561561+ | Simdjsont.Json.Object pairs -> (
529562 match List.assoc_opt "$type" pairs with
530530- | Some (`String type_ref) ->
563563+ | Some (Simdjsont.Json.String type_ref) ->
531564 let errs = ref [] in
532565 (* Check if type is in allowed refs for closed unions *)
533566 (if union.closed then
+34-27
test/lexicon/test_lexicon.ml
···1111 let ic = open_in path in
1212 let content = really_input_string ic (in_channel_length ic) in
1313 close_in ic;
1414- Yojson.Basic.from_string content
1414+ match Simdjsont.decode Simdjsont.Codec.value content with
1515+ | Ok json -> json
1616+ | Error msg -> failwith msg
15171618(** Read catalog lexicon file *)
1719let read_catalog_file filename =
···2325let test_valid_lexicons () =
2426 let fixtures = read_fixture_json "lexicon-valid.json" in
2527 match fixtures with
2626- | `List items ->
2828+ | Simdjsont.Json.Array items ->
2729 List.iter
2830 (fun item ->
2931 match item with
3030- | `Assoc pairs -> (
3232+ | Simdjsont.Json.Object pairs -> (
3133 let name =
3234 match List.assoc_opt "name" pairs with
3333- | Some (`String s) -> s
3535+ | Some (Simdjsont.Json.String s) -> s
3436 | _ -> "unknown"
3537 in
3638 match List.assoc_opt "lexicon" pairs with
3739 | Some lexicon_json -> (
3838- let lexicon_str = Yojson.Basic.to_string lexicon_json in
4040+ let lexicon_str = Simdjsont.Json.to_string lexicon_json in
3941 match Parser.of_string lexicon_str with
4042 | Ok lexicon ->
4143 Alcotest.(check bool)
···5456let test_invalid_lexicons () =
5557 let fixtures = read_fixture_json "lexicon-invalid.json" in
5658 match fixtures with
5757- | `List items ->
5959+ | Simdjsont.Json.Array items ->
5860 List.iter
5961 (fun item ->
6062 match item with
6161- | `Assoc pairs -> (
6363+ | Simdjsont.Json.Object pairs -> (
6264 let name =
6365 match List.assoc_opt "name" pairs with
6464- | Some (`String s) -> s
6666+ | Some (Simdjsont.Json.String s) -> s
6567 | _ -> "unknown"
6668 in
6769 match List.assoc_opt "lexicon" pairs with
6870 | Some lexicon_json -> (
6969- let lexicon_str = Yojson.Basic.to_string lexicon_json in
7171+ let lexicon_str = Simdjsont.Json.to_string lexicon_json in
7072 match Parser.of_string lexicon_str with
7173 | Ok _ ->
7274 (* Some "invalid" lexicons may be parseable but semantically invalid *)
···313315 let resolver = make_resolver lexicon in
314316 let fixtures = read_fixture_json "record-data-valid.json" in
315317 match fixtures with
316316- | `List items ->
318318+ | Simdjsont.Json.Array items ->
317319 List.iter
318320 (fun item ->
319321 match item with
320320- | `Assoc pairs -> (
322322+ | Simdjsont.Json.Object pairs -> (
321323 let name =
322324 match List.assoc_opt "name" pairs with
323323- | Some (`String s) -> s
325325+ | Some (Simdjsont.Json.String s) -> s
324326 | _ -> "unknown"
325327 in
326328 match List.assoc_opt "data" pairs with
···352354 let resolver = make_resolver lexicon in
353355 let fixtures = read_fixture_json "record-data-invalid.json" in
354356 match fixtures with
355355- | `List items ->
357357+ | Simdjsont.Json.Array items ->
356358 List.iter
357359 (fun item ->
358360 match item with
359359- | `Assoc pairs -> (
361361+ | Simdjsont.Json.Object pairs -> (
360362 let name =
361363 match List.assoc_opt "name" pairs with
362362- | Some (`String s) -> s
364364+ | Some (Simdjsont.Json.String s) -> s
363365 | _ -> "unknown"
364366 in
365367 match List.assoc_opt "data" pairs with
···386388 | None -> Alcotest.fail "could not load record schema"
387389 | Some schema ->
388390 (* Missing required 'integer' field *)
389389- let data = `Assoc [ ("$type", `String "example.lexicon.record") ] in
391391+ let data =
392392+ Simdjsont.Json.Object
393393+ [ ("$type", Simdjsont.Json.String "example.lexicon.record") ]
394394+ in
390395 let errors = Validator.validate_record ~path:[] schema data in
391396 Alcotest.(check bool) "has errors" true (errors <> []);
392397 let has_required_error =
···403408 | Some schema ->
404409 (* Wrong type for integer field *)
405410 let data =
406406- `Assoc
411411+ Simdjsont.Json.Object
407412 [
408408- ("$type", `String "example.lexicon.record");
409409- ("integer", `String "not-an-integer");
413413+ ("$type", Simdjsont.Json.String "example.lexicon.record");
414414+ ("integer", Simdjsont.Json.String "not-an-integer");
410415 ]
411416 in
412417 let errors = Validator.validate_record ~path:[] schema data in
···425430 | Some schema ->
426431 (* Invalid DID format in nested formats object *)
427432 let data =
428428- `Assoc
433433+ Simdjsont.Json.Object
429434 [
430430- ("$type", `String "example.lexicon.record");
431431- ("integer", `Int 1);
432432- ("formats", `Assoc [ ("did", `String "invalid-did") ]);
435435+ ("$type", Simdjsont.Json.String "example.lexicon.record");
436436+ ("integer", Simdjsont.Json.Int 1L);
437437+ ( "formats",
438438+ Simdjsont.Json.Object
439439+ [ ("did", Simdjsont.Json.String "invalid-did") ] );
433440 ]
434441 in
435442 let _errors = Validator.validate_record ~path:[] schema data in
···443450 | Some schema ->
444451 (* Integer out of range *)
445452 let data =
446446- `Assoc
453453+ Simdjsont.Json.Object
447454 [
448448- ("$type", `String "example.lexicon.record");
449449- ("integer", `Int 1);
450450- ("rangeInteger", `Int 9000);
455455+ ("$type", Simdjsont.Json.String "example.lexicon.record");
456456+ ("integer", Simdjsont.Json.Int 1L);
457457+ ("rangeInteger", Simdjsont.Json.Int 9000L);
451458 ]
452459 in
453460 let errors = Validator.validate_record ~path:[] schema data in