+20
-32
bin/jsonpp.ml
+20
-32
bin/jsonpp.ml
···
17
17
| Ok s -> s
18
18
| Error e -> failwith e
19
19
20
-
(* Helper to get indices from either nav or append pointer *)
21
-
let indices_of_result (result : [ `Nav of Jsont_pointer.nav Jsont_pointer.t
22
-
| `Append of Jsont_pointer.append Jsont_pointer.t ]) =
23
-
match result with
24
-
| `Nav p -> Jsont_pointer.indices p
25
-
| `Append p -> Jsont_pointer.indices p
20
+
(* Helper to get indices from any pointer *)
21
+
let indices_of_any (Jsont_pointer.Any p) = Jsont_pointer.indices p
26
22
27
-
(* Helper to convert to string from either nav or append pointer *)
28
-
let to_string_of_result (result : [ `Nav of Jsont_pointer.nav Jsont_pointer.t
29
-
| `Append of Jsont_pointer.append Jsont_pointer.t ]) =
30
-
match result with
31
-
| `Nav p -> Jsont_pointer.to_string p
32
-
| `Append p -> Jsont_pointer.to_string p
23
+
(* Helper to convert to string from any pointer *)
24
+
let to_string_of_any (Jsont_pointer.Any p) = Jsont_pointer.to_string p
25
+
26
+
(* Helper to check if pointer is append *)
27
+
let is_append_any (Jsont_pointer.Any p : Jsont_pointer.any) =
28
+
not (Jsont_pointer.is_nav (Jsont_pointer.Any p))
33
29
34
30
(* Test: parse pointer and print indices *)
35
31
let test_parse pointer_str =
36
32
try
37
33
let result = Jsont_pointer.of_string pointer_str in
38
-
let indices = indices_of_result result in
34
+
let indices = indices_of_any result in
39
35
let index_strs = List.map (fun idx ->
40
36
match idx with
41
37
| Jsont.Path.Mem (s, _) -> Printf.sprintf "Mem:%s" s
42
38
| Jsont.Path.Nth (n, _) -> Printf.sprintf "Nth:%d" n
43
39
) indices in
44
-
let suffix = match result with `Nav _ -> "" | `Append _ -> ", /-" in
40
+
let suffix = if is_append_any result then ", /-" else "" in
45
41
Printf.printf "OK: [%s%s]\n" (String.concat ", " index_strs) suffix
46
42
with Jsont.Error e ->
47
43
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
50
46
let test_roundtrip pointer_str =
51
47
try
52
48
let result = Jsont_pointer.of_string pointer_str in
53
-
let s = to_string_of_result result in
49
+
let s = to_string_of_any result in
54
50
if s = pointer_str then
55
51
Printf.printf "OK: %s\n" s
56
52
else
···
88
84
let test_uri_fragment pointer_str =
89
85
try
90
86
let result = Jsont_pointer.of_string pointer_str in
91
-
let frag = match result with
92
-
| `Nav p -> Jsont_pointer.to_uri_fragment p
93
-
| `Append p -> Jsont_pointer.to_uri_fragment p
94
-
in
87
+
let (Jsont_pointer.Any p) = result in
88
+
let frag = Jsont_pointer.to_uri_fragment p in
95
89
let result2 = Jsont_pointer.of_uri_fragment frag in
96
-
let s2 = to_string_of_result result2 in
90
+
let s2 = to_string_of_any result2 in
97
91
if s2 = pointer_str then
98
92
Printf.printf "OK: %s -> %s\n" pointer_str frag
99
93
else
···
106
100
try
107
101
let json = parse_json json_str in
108
102
let value = parse_json value_str in
109
-
let result = match Jsont_pointer.of_string pointer_str with
110
-
| `Nav p -> Jsont_pointer.add p json ~value
111
-
| `Append p -> Jsont_pointer.add p json ~value
112
-
in
103
+
let p = Jsont_pointer.of_string pointer_str in
104
+
let result = Jsont_pointer.add p json ~value in
113
105
Printf.printf "%s\n" (json_to_string result)
114
106
with Jsont.Error e ->
115
107
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
140
132
try
141
133
let json = parse_json json_str in
142
134
let from = Jsont_pointer.of_string_nav from_str in
143
-
let result = match Jsont_pointer.of_string path_str with
144
-
| `Nav path -> Jsont_pointer.move ~from ~path json
145
-
| `Append path -> Jsont_pointer.move ~from ~path json
146
-
in
135
+
let path = Jsont_pointer.of_string path_str in
136
+
let result = Jsont_pointer.move ~from ~path json in
147
137
Printf.printf "%s\n" (json_to_string result)
148
138
with Jsont.Error e ->
149
139
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
153
143
try
154
144
let json = parse_json json_str in
155
145
let from = Jsont_pointer.of_string_nav from_str in
156
-
let result = match Jsont_pointer.of_string path_str with
157
-
| `Nav path -> Jsont_pointer.copy ~from ~path json
158
-
| `Append path -> Jsont_pointer.copy ~from ~path json
159
-
in
146
+
let path = Jsont_pointer.of_string path_str in
147
+
let result = Jsont_pointer.copy ~from ~path json in
160
148
Printf.printf "%s\n" (json_to_string result)
161
149
with Jsont.Error e ->
162
150
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
+152
-102
doc/tutorial.mld
+152
-102
doc/tutorial.mld
···
54
54
For example, given this JSON document:
55
55
56
56
{x@ocaml[
57
-
# let users_json =
58
-
parse_json "{\"users\":[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]}";;
57
+
# let users_json = parse_json {|{
58
+
"users": [
59
+
{"name": "Alice", "age": 30},
60
+
{"name": "Bob", "age": 25}
61
+
]
62
+
}|};;
59
63
val users_json : Jsont.json =
60
64
{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}
61
65
]x}
···
129
133
130
134
Each reference token is represented using {!Jsont.Path.index}:
131
135
132
-
{[
136
+
{v
133
137
type index = Jsont.Path.index
134
138
(* = Jsont.Path.Mem of string * Jsont.Meta.t
135
139
| Jsont.Path.Nth of int * Jsont.Meta.t *)
136
-
]}
140
+
v}
137
141
138
142
The [Mem] constructor is for object member access, and [Nth] is for array
139
143
index access. The member name is {b unescaped} - you work with the actual
···
156
160
157
161
{@ocaml[
158
162
# of_string_result "foo";;
159
-
- : ([ `Append of append t | `Nav of nav t ], string) result =
163
+
- : (any, string) result =
160
164
Error "Invalid JSON Pointer: must be empty or start with '/': foo"
161
165
# of_string_result "/valid";;
162
-
- : ([ `Append of append t | `Nav of nav t ], string) result =
163
-
Ok (`Nav [Mem "valid"])
166
+
- : (any, string) result = Ok (Any <abstr>)
164
167
]}
165
168
166
169
{1 Evaluation: Navigating JSON}
···
175
178
176
179
Let's use the example JSON document from RFC 6901, Section 5:
177
180
178
-
{@ocaml[
179
-
# let rfc_example = parse_json "{\"foo\":[\"bar\",\"baz\"],\"\":0,\"a/b\":1,\"c%d\":2,\"e^f\":3,\"g|h\":4,\"i\\\\j\":5,\"k\\\"l\":6,\" \":7,\"m~n\":8}";;
181
+
{x@ocaml[
182
+
# let rfc_example = parse_json {|{
183
+
"foo": ["bar", "baz"],
184
+
"": 0,
185
+
"a/b": 1,
186
+
"c%d": 2,
187
+
"e^f": 3,
188
+
"g|h": 4,
189
+
"i\\j": 5,
190
+
"k\"l": 6,
191
+
" ": 7,
192
+
"m~n": 8
193
+
}|};;
180
194
val rfc_example : Jsont.json =
181
195
{"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8}
182
-
]}
196
+
]x}
183
197
184
198
This document is carefully constructed to exercise various edge cases!
185
199
···
304
318
305
319
The library provides both exception-raising and result-returning variants:
306
320
307
-
[
321
+
{v
308
322
val get : nav t -> Jsont.json -> Jsont.json
309
323
val get_result : nav t -> Jsont.json -> (Jsont.json, Jsont.Error.t) result
310
324
val find : nav t -> Jsont.json -> Jsont.json option
311
-
]
325
+
v}
312
326
313
327
{2 Array Index Rules}
314
328
···
355
369
between pointers that can be used for navigation and pointers that target
356
370
the "append position":
357
371
358
-
{[
372
+
{v
359
373
type nav (* A pointer to an existing element *)
360
374
type append (* A pointer ending with "-" (append position) *)
361
375
type 'a t (* Pointer with phantom type parameter *)
362
-
]}
376
+
type any (* Existential: wraps either nav or append *)
377
+
v}
363
378
364
-
When you parse a pointer, you get either a [nav t] or an [append t]:
379
+
When you parse a pointer with {!of_string}, you get an {!any} pointer
380
+
that can be used directly with mutation operations:
365
381
366
382
{@ocaml[
367
383
# of_string "/foo/0";;
368
-
- : [ `Append of Jsont_pointer.append Jsont_pointer.t
369
-
| `Nav of Jsont_pointer.nav Jsont_pointer.t ]
370
-
= `Nav [Mem "foo"; Nth 0]
384
+
- : any = Any <abstr>
371
385
# of_string "/foo/-";;
372
-
- : [ `Append of Jsont_pointer.append Jsont_pointer.t
373
-
| `Nav of Jsont_pointer.nav Jsont_pointer.t ]
374
-
= `Append [Mem "foo"] /-
386
+
- : any = Any <abstr>
375
387
]}
376
388
377
-
The [-] creates an [append] pointer. Note that in the internal
378
-
representation, the append position is tracked separately (shown as [/-]).
389
+
The [-] creates an append pointer. The {!any} type wraps either kind,
390
+
making it ergonomic to use with operations like {!set} and {!add}.
379
391
380
-
{2 Why Phantom Types?}
392
+
{2 Why Two Pointer Types?}
381
393
382
394
The RFC explains that [-] refers to a {e nonexistent} position:
383
395
···
387
399
388
400
So you {b cannot use [get] or [find]} with an append pointer - it makes
389
401
no sense to retrieve a value from a position that doesn't exist! The
390
-
library enforces this at compile time.
402
+
library enforces this:
403
+
- Use {!of_string_nav} when you need to call {!get} or {!find}
404
+
- Use {!of_string} (returns {!any}) for mutation operations
391
405
392
-
However, append pointers {b are} valid for mutation operations like {!add}:
406
+
Mutation operations like {!add} accept {!any} directly:
393
407
394
408
{x@ocaml[
395
-
# let arr_obj = parse_json "{\"foo\":[\"a\",\"b\"]}";;
409
+
# let arr_obj = parse_json {|{"foo":["a","b"]}|};;
396
410
val arr_obj : Jsont.json = {"foo":["a","b"]}
397
-
# (match of_string "/foo/-" with `Append p -> add p arr_obj ~value:(Jsont.Json.string "c") | `Nav _ -> assert false);;
411
+
# add (of_string "/foo/-") arr_obj ~value:(Jsont.Json.string "c");;
398
412
- : Jsont.json = {"foo":["a","b","c"]}
399
413
]x}
400
414
401
-
For convenience, use {!of_string_nav} when you know a pointer shouldn't
402
-
contain [-]:
415
+
For retrieval operations, use {!of_string_nav} which ensures the pointer
416
+
doesn't contain [-]:
403
417
404
418
{@ocaml[
405
419
# of_string_nav "/foo/0";;
406
-
- : Jsont_pointer.nav Jsont_pointer.t = [Mem "foo"; Nth 0]
420
+
- : nav t = [Mem "foo"; Nth 0]
407
421
# of_string_nav "/foo/-";;
408
422
Exception:
409
423
Jsont.Error Invalid JSON Pointer: '-' not allowed in navigation pointer.
···
415
429
416
430
{@ocaml[
417
431
# let nav_ptr = of_string_nav "/foo";;
418
-
val nav_ptr : Jsont_pointer.nav Jsont_pointer.t = [Mem "foo"]
432
+
val nav_ptr : nav t = [Mem "foo"]
419
433
# let app_ptr = at_end nav_ptr;;
420
-
val app_ptr : Jsont_pointer.append Jsont_pointer.t = [Mem "foo"] /-
434
+
val app_ptr : append t = [Mem "foo"] /-
421
435
# to_string app_ptr;;
422
436
- : string = "/foo/-"
423
437
]}
···
428
442
(JSON Patch) uses JSON Pointer for modifications. The [jsont-pointer]
429
443
library provides these operations.
430
444
431
-
{2 Which Pointer Type for Which Operation?}
432
-
433
-
The phantom type system enforces correct usage:
434
-
435
-
{ul
436
-
{- {!get}, {!find} - [nav t] only - Can't retrieve from non-existent position}
437
-
{- {!remove} - [nav t] only - Can't remove what doesn't exist}
438
-
{- {!replace} - [nav t] only - Can't replace what doesn't exist}
439
-
{- {!test} - [nav t] only - Can't test non-existent position}
440
-
{- {!add} - [_ t] (both) - Can add at existing position OR append}
441
-
{- {!set} - [_ t] (both) - Can set existing position OR append}
442
-
{- {!move}, {!copy} - [from:nav t], [path:_ t] - Source must exist, dest can be append}
443
-
}
444
-
445
445
{2 Add}
446
446
447
-
The {!add} operation inserts a value at a location:
447
+
The {!add} operation inserts a value at a location. It accepts {!any}
448
+
pointers, so you can use {!of_string} directly:
448
449
449
-
{@ocaml[
450
-
# let obj = parse_json "{\"foo\":\"bar\"}";;
450
+
{x@ocaml[
451
+
# let obj = parse_json {|{"foo":"bar"}|};;
451
452
val obj : Jsont.json = {"foo":"bar"}
452
-
# add (of_string_nav "/baz") obj ~value:(Jsont.Json.string "qux")
453
-
;;
453
+
# add (of_string "/baz") obj ~value:(Jsont.Json.string "qux");;
454
454
- : Jsont.json = {"foo":"bar","baz":"qux"}
455
-
]}
455
+
]x}
456
456
457
457
For arrays, {!add} inserts BEFORE the specified index:
458
458
459
459
{x@ocaml[
460
-
# let arr_obj = parse_json "{\"foo\":[\"a\",\"b\"]}";;
460
+
# let arr_obj = parse_json {|{"foo":["a","b"]}|};;
461
461
val arr_obj : Jsont.json = {"foo":["a","b"]}
462
-
# add (of_string_nav "/foo/1") arr_obj ~value:(Jsont.Json.string "X")
463
-
;;
462
+
# add (of_string "/foo/1") arr_obj ~value:(Jsont.Json.string "X");;
464
463
- : Jsont.json = {"foo":["a","X","b"]}
465
464
]x}
466
465
467
-
This is where the [-] marker and append pointers shine - they append to the end:
466
+
This is where the [-] marker shines - it appends to the end:
468
467
469
468
{x@ocaml[
470
-
# (match of_string "/foo/-" with `Append p -> add p arr_obj ~value:(Jsont.Json.string "c") | `Nav _ -> assert false);;
469
+
# add (of_string "/foo/-") arr_obj ~value:(Jsont.Json.string "c");;
471
470
- : Jsont.json = {"foo":["a","b","c"]}
472
471
]x}
473
472
474
-
Or more conveniently using {!at_end}:
473
+
You can also use {!at_end} to create an append pointer programmatically:
475
474
476
475
{x@ocaml[
477
-
# add (at_end (of_string_nav "/foo")) arr_obj ~value:(Jsont.Json.string "c")
478
-
;;
476
+
# add (any (at_end (of_string_nav "/foo"))) arr_obj ~value:(Jsont.Json.string "c");;
479
477
- : Jsont.json = {"foo":["a","b","c"]}
480
478
]x}
481
479
480
+
{2 Ergonomic Mutation with [any]}
481
+
482
+
Since {!add}, {!set}, {!move}, and {!copy} accept {!any} pointers, you can
483
+
use {!of_string} directly without any pattern matching. This makes JSON
484
+
Patch implementations straightforward:
485
+
486
+
{x@ocaml[
487
+
# let items = parse_json {|{"items":["x"]}|};;
488
+
val items : Jsont.json = {"items":["x"]}
489
+
# add (of_string "/items/0") items ~value:(Jsont.Json.string "y");;
490
+
- : Jsont.json = {"items":["y","x"]}
491
+
# add (of_string "/items/-") items ~value:(Jsont.Json.string "z");;
492
+
- : Jsont.json = {"items":["x","z"]}
493
+
]x}
494
+
495
+
The same pointer works whether it targets an existing position or the
496
+
append marker - no conditional logic needed.
497
+
482
498
{2 Remove}
483
499
484
500
The {!remove} operation deletes a value. It only accepts [nav t] because
485
501
you can only remove something that exists:
486
502
487
-
{@ocaml[
488
-
# let two_fields = parse_json "{\"foo\":\"bar\",\"baz\":\"qux\"}";;
503
+
{x@ocaml[
504
+
# let two_fields = parse_json {|{"foo":"bar","baz":"qux"}|};;
489
505
val two_fields : Jsont.json = {"foo":"bar","baz":"qux"}
490
506
# remove (of_string_nav "/baz") two_fields ;;
491
507
- : Jsont.json = {"foo":"bar"}
492
-
]}
508
+
]x}
493
509
494
510
For arrays, it removes and shifts:
495
511
496
512
{x@ocaml[
497
-
# let three_elem = parse_json "{\"foo\":[\"a\",\"b\",\"c\"]}";;
513
+
# let three_elem = parse_json {|{"foo":["a","b","c"]}|};;
498
514
val three_elem : Jsont.json = {"foo":["a","b","c"]}
499
515
# remove (of_string_nav "/foo/1") three_elem ;;
500
516
- : Jsont.json = {"foo":["a","c"]}
···
516
532
{2 Move}
517
533
518
534
The {!move} operation relocates a value. The source ([from]) must be a [nav t]
519
-
(you can only move something that exists), but the destination ([path]) can
520
-
be either:
535
+
(you can only move something that exists), but the destination ([path])
536
+
accepts {!any}:
521
537
522
-
{@ocaml[
523
-
# let nested = parse_json "{\"foo\":{\"bar\":\"baz\"},\"qux\":{}}";;
538
+
{x@ocaml[
539
+
# let nested = parse_json {|{"foo":{"bar":"baz"},"qux":{}}|};;
524
540
val nested : Jsont.json = {"foo":{"bar":"baz"},"qux":{}}
525
-
# move ~from:(of_string_nav "/foo/bar") ~path:(of_string_nav "/qux/thud") nested
526
-
;;
541
+
# move ~from:(of_string_nav "/foo/bar") ~path:(of_string "/qux/thud") nested;;
527
542
- : Jsont.json = {"foo":{},"qux":{"thud":"baz"}}
528
-
]}
543
+
]x}
529
544
530
545
{2 Copy}
531
546
532
547
The {!copy} operation duplicates a value (same typing as {!move}):
533
548
534
-
{@ocaml[
535
-
# let to_copy = parse_json "{\"foo\":{\"bar\":\"baz\"}}";;
549
+
{x@ocaml[
550
+
# let to_copy = parse_json {|{"foo":{"bar":"baz"}}|};;
536
551
val to_copy : Jsont.json = {"foo":{"bar":"baz"}}
537
-
# copy ~from:(of_string_nav "/foo/bar") ~path:(of_string_nav "/foo/qux") to_copy
538
-
;;
552
+
# copy ~from:(of_string_nav "/foo/bar") ~path:(of_string "/foo/qux") to_copy;;
539
553
- : Jsont.json = {"foo":{"bar":"baz","qux":"baz"}}
540
-
]}
554
+
]x}
541
555
542
556
{2 Test}
543
557
···
574
588
575
589
{@ocaml[
576
590
# let p = make [mem "a/b"];;
577
-
val p : Jsont_pointer.nav Jsont_pointer.t = [Mem "a/b"]
591
+
val p : nav t = [Mem "a/b"]
578
592
# to_string p;;
579
593
- : string = "/a~1b"
580
594
# of_string_nav "/a~1b";;
581
-
- : Jsont_pointer.nav Jsont_pointer.t = [Mem "a/b"]
595
+
- : nav t = [Mem "a/b"]
582
596
]}
583
597
584
598
{2 Escaping in Action}
···
657
671
Here's the RFC example showing the URI fragment forms:
658
672
659
673
{ul
660
-
{- [""] → [#] → whole document}
661
-
{- ["/foo"] → [#/foo] → [["bar", "baz"]]}
662
-
{- ["/foo/0"] → [#/foo/0] → ["bar"]}
663
-
{- ["/"] → [#/] → [0]}
664
-
{- ["/a~1b"] → [#/a~1b] → [1]}
665
-
{- ["/c%d"] → [#/c%25d] → [2]}
666
-
{- ["/ "] → [#/%20] → [7]}
667
-
{- ["/m~0n"] → [#/m~0n] → [8]}
674
+
{- [""] -> [#] -> whole document}
675
+
{- ["/foo"] -> [#/foo] -> [["bar", "baz"]]}
676
+
{- ["/foo/0"] -> [#/foo/0] -> ["bar"]}
677
+
{- ["/"] -> [#/] -> [0]}
678
+
{- ["/a~1b"] -> [#/a~1b] -> [1]}
679
+
{- ["/c%d"] -> [#/c%25d] -> [2]}
680
+
{- ["/ "] -> [#/%20] -> [7]}
681
+
{- ["/m~0n"] -> [#/m~0n] -> [8]}
668
682
}
669
683
670
684
{1 Building Pointers Programmatically}
···
673
687
674
688
{@ocaml[
675
689
# let port_ptr = make [mem "database"; mem "port"];;
676
-
val port_ptr : Jsont_pointer.nav Jsont_pointer.t =
677
-
[Mem "database"; Mem "port"]
690
+
val port_ptr : nav t = [Mem "database"; Mem "port"]
678
691
# to_string port_ptr;;
679
692
- : string = "/database/port"
680
693
]}
···
683
696
684
697
{@ocaml[
685
698
# let first_feature_ptr = make [mem "features"; nth 0];;
686
-
val first_feature_ptr : Jsont_pointer.nav Jsont_pointer.t =
687
-
[Mem "features"; Nth 0]
699
+
val first_feature_ptr : nav t = [Mem "features"; Nth 0]
688
700
# to_string first_feature_ptr;;
689
701
- : string = "/features/0"
690
702
]}
···
695
707
696
708
{@ocaml[
697
709
# let db_ptr = of_string_nav "/database";;
698
-
val db_ptr : Jsont_pointer.nav Jsont_pointer.t = [Mem "database"]
710
+
val db_ptr : nav t = [Mem "database"]
699
711
# let creds_ptr = db_ptr / mem "credentials";;
700
-
val creds_ptr : Jsont_pointer.nav Jsont_pointer.t =
701
-
[Mem "database"; Mem "credentials"]
712
+
val creds_ptr : nav t = [Mem "database"; Mem "credentials"]
702
713
# let user_ptr = creds_ptr / mem "username";;
703
-
val user_ptr : Jsont_pointer.nav Jsont_pointer.t =
704
-
[Mem "database"; Mem "credentials"; Mem "username"]
714
+
val user_ptr : nav t = [Mem "database"; Mem "credentials"; Mem "username"]
705
715
# to_string user_ptr;;
706
716
- : string = "/database/credentials/username"
707
717
]}
···
710
720
711
721
{@ocaml[
712
722
# let base = of_string_nav "/api/v1";;
713
-
val base : Jsont_pointer.nav Jsont_pointer.t = [Mem "api"; Mem "v1"]
723
+
val base : nav t = [Mem "api"; Mem "v1"]
714
724
# let endpoint = of_string_nav "/users/0";;
715
-
val endpoint : Jsont_pointer.nav Jsont_pointer.t = [Mem "users"; Nth 0]
725
+
val endpoint : nav t = [Mem "users"; Nth 0]
716
726
# to_string (concat base endpoint);;
717
727
- : string = "/api/v1/users/0"
718
728
]}
···
725
735
directly to an OCaml type.
726
736
727
737
{x@ocaml[
728
-
# let config_json = parse_json "{\"database\":{\"host\":\"localhost\",\"port\":5432,\"credentials\":{\"username\":\"admin\",\"password\":\"secret\"}},\"features\":[\"auth\",\"logging\",\"metrics\"]}";;
738
+
# let config_json = parse_json {|{
739
+
"database": {
740
+
"host": "localhost",
741
+
"port": 5432,
742
+
"credentials": {"username": "admin", "password": "secret"}
743
+
},
744
+
"features": ["auth", "logging", "metrics"]
745
+
}|};;
729
746
val config_json : Jsont.json =
730
747
{"database":{"host":"localhost","port":5432,"credentials":{"username":"admin","password":"secret"}},"features":["auth","logging","metrics"]}
731
748
]x}
···
735
752
The {!path} combinator combines pointer navigation with typed decoding:
736
753
737
754
{@ocaml[
755
+
# let nav = of_string_nav "/database/host";;
756
+
val nav : nav t = [Mem "database"; Mem "host"]
738
757
# let db_host =
739
758
Jsont.Json.decode
740
-
(path (of_string_nav "/database/host") Jsont.string)
759
+
(path nav Jsont.string)
741
760
config_json
742
761
|> Result.get_ok;;
743
762
val db_host : string = "localhost"
···
778
797
You can extract values from deeply nested structures:
779
798
780
799
{x@ocaml[
781
-
# let org_json = parse_json "{\"organization\":{\"owner\":{\"name\":\"Alice\",\"email\":\"alice@example.com\",\"age\":35},\"members\":[{\"name\":\"Bob\",\"email\":\"bob@example.com\",\"age\":28}]}}";;
800
+
# let org_json = parse_json {|{
801
+
"organization": {
802
+
"owner": {"name": "Alice", "email": "alice@example.com", "age": 35},
803
+
"members": [{"name": "Bob", "email": "bob@example.com", "age": 28}]
804
+
}
805
+
}|};;
782
806
val org_json : Jsont.json =
783
807
{"organization":{"owner":{"name":"Alice","email":"alice@example.com","age":35},"members":[{"name":"Bob","email":"bob@example.com","age":28}]}}
784
808
# Jsont.Json.decode
···
818
842
819
843
The typed approach catches mismatches at decode time with clear errors.
820
844
845
+
{2 Updates with Polymorphic Pointers}
846
+
847
+
The {!set} and {!add} functions accept {!any} pointers, which means you can
848
+
use the result of {!of_string} directly without pattern matching:
849
+
850
+
{x@ocaml[
851
+
# let tasks = parse_json {|{"tasks":["buy milk"]}|};;
852
+
val tasks : Jsont.json = {"tasks":["buy milk"]}
853
+
# set (of_string "/tasks/0") tasks ~value:(Jsont.Json.string "buy eggs");;
854
+
- : Jsont.json = {"tasks":["buy eggs"]}
855
+
# set (of_string "/tasks/-") tasks ~value:(Jsont.Json.string "call mom");;
856
+
- : Jsont.json = {"tasks":["buy milk","call mom"]}
857
+
]x}
858
+
859
+
This is useful for implementing JSON Patch ([RFC 6902]) where
860
+
operations like ["add"] can target either existing positions or the
861
+
append marker. If you need to distinguish between pointer types at runtime,
862
+
use {!of_string_kind} which returns a polymorphic variant:
863
+
864
+
{x@ocaml[
865
+
# of_string_kind "/tasks/0";;
866
+
- : [ `Append of append t | `Nav of nav t ] = `Nav [Mem "tasks"; Nth 0]
867
+
# of_string_kind "/tasks/-";;
868
+
- : [ `Append of append t | `Nav of nav t ] = `Append [Mem "tasks"] /-
869
+
]x}
870
+
821
871
{1 Summary}
822
872
823
873
JSON Pointer (RFC 6901) provides a simple but powerful way to address
···
829
879
{- {b Evaluation}: Tokens navigate through objects (by key) and arrays (by index)}
830
880
{- {b URI Encoding}: Pointers can be percent-encoded for use in URIs}
831
881
{- {b Mutations}: Combined with JSON Patch (RFC 6902), pointers enable structured updates}
832
-
{- {b Type Safety}: Phantom types ([nav t] vs [append t]) prevent misuse of append pointers with retrieval operations}
882
+
{- {b Type Safety}: Phantom types ([nav t] vs [append t]) prevent misuse of append pointers with retrieval operations, while the [any] existential type allows ergonomic use with mutation operations}
833
883
}
834
884
835
885
The [jsont-pointer] library implements all of this with type-safe OCaml
+51
-20
src/jsont_pointer.ml
+51
-20
src/jsont_pointer.ml
···
104
104
is_append : bool; (* true if ends with "-" *)
105
105
}
106
106
107
+
(* Existential wrapper *)
108
+
type any = Any : _ t -> any
109
+
107
110
let root = { segments = []; is_append = false }
108
111
109
112
let is_root p = p.segments = [] && not p.is_append
···
134
137
135
138
let indices (type a) (p : a t) = List.map Segment.to_index p.segments
136
139
140
+
(* Coercion and inspection *)
141
+
142
+
let any (type a) (p : a t) : any = Any p
143
+
144
+
let is_nav (Any p) = not p.is_append
145
+
146
+
let to_nav (Any p) =
147
+
if p.is_append then None
148
+
else Some { segments = p.segments; is_append = false }
149
+
150
+
let to_nav_exn (Any p) =
151
+
if p.is_append then
152
+
Jsont.Error.msgf Jsont.Meta.none
153
+
"JSON Pointer: cannot convert append pointer to nav pointer"
154
+
else
155
+
{ segments = p.segments; is_append = false }
156
+
137
157
(* Parsing *)
138
158
139
159
let parse_segments s =
···
146
166
let tokens = String.split_on_char '/' rest in
147
167
List.map Segment.of_escaped_string tokens
148
168
149
-
let of_string s : [ `Nav of nav t | `Append of append t ] =
169
+
let of_string_kind s : [ `Nav of nav t | `Append of append t ] =
150
170
let segments = parse_segments s in
151
171
(* Check if ends with "-" *)
152
172
match List.rev segments with
···
163
183
"Invalid JSON Pointer: '-' can only appear at the end";
164
184
`Nav { segments; is_append = false }
165
185
186
+
let of_string s : any =
187
+
match of_string_kind s with
188
+
| `Nav p -> Any p
189
+
| `Append p -> Any p
190
+
166
191
let of_string_nav s : nav t =
167
-
match of_string s with
192
+
match of_string_kind s with
168
193
| `Nav p -> p
169
194
| `Append _ ->
170
195
Jsont.Error.msgf Jsont.Meta.none
···
203
228
in
204
229
loop 0
205
230
206
-
let of_uri_fragment s = of_string (percent_decode s)
231
+
let of_uri_fragment s : any = of_string (percent_decode s)
207
232
208
233
let of_uri_fragment_nav s = of_string_nav (percent_decode s)
209
234
210
-
let of_uri_fragment_result s =
235
+
let of_uri_fragment_result s : (any, string) result =
211
236
try Ok (of_uri_fragment s)
212
237
with Jsont.Error e -> Error (Jsont.Error.to_string e)
213
238
···
467
492
| None -> error_index_out_of_bounds json n)
468
493
~on_other:(fun () -> error_cannot_navigate json)
469
494
470
-
let set (type a) (p : a t) json ~value =
495
+
let set (Any p) json ~value =
471
496
eval_set_segments p.segments p.is_append value json
472
497
473
498
(* Mutation: add (RFC 6902 semantics) - works with any pointer type *)
···
528
553
| None -> error_index_out_of_bounds json n)
529
554
~on_other:(fun () -> error_cannot_navigate json)
530
555
531
-
let add (type a) (p : a t) json ~value =
556
+
let add (Any p) json ~value =
532
557
eval_add_segments p.segments p.is_append value json
533
558
534
559
(* Mutation: remove - only for nav pointers *)
···
578
603
579
604
(* Mutation: move *)
580
605
581
-
let move ~(from : nav t) ~(path : _ t) json =
606
+
let move ~(from : nav t) ~(path : any) json =
607
+
let (Any p) = path in
582
608
(* Check for cycle: path cannot be a proper prefix of from *)
583
609
let from_segs = from.segments in
584
-
let path_segs = path.segments in
610
+
let path_segs = p.segments in
585
611
let rec is_prefix p1 p2 = match p1, p2 with
586
612
| [], _ -> true
587
613
| _, [] -> false
588
614
| h1 :: t1, h2 :: t2 -> String.equal h1 h2 && is_prefix t1 t2
589
615
in
590
616
if is_prefix path_segs from_segs &&
591
-
not (List.equal String.equal path_segs from_segs && path.is_append = false) then
617
+
not (List.equal String.equal path_segs from_segs && p.is_append = false) then
592
618
Jsont.Error.msgf Jsont.Meta.none
593
619
"JSON Pointer: move would create cycle (path is prefix of from)";
594
620
let value = get from json in
···
597
623
598
624
(* Mutation: copy *)
599
625
600
-
let copy ~(from : nav t) ~(path : _ t) json =
626
+
let copy ~(from : nav t) ~(path : any) json =
601
627
let value = get from json in
602
628
add path json ~value
603
629
···
608
634
609
635
(* Jsont codec *)
610
636
611
-
let jsont : [ `Nav of nav t | `Append of append t ] Jsont.t =
637
+
let jsont : any Jsont.t =
612
638
let dec _meta s = of_string s in
639
+
let enc (Any p) = to_string p in
640
+
Jsont.Base.string (Jsont.Base.map
641
+
~kind:"JSON Pointer"
642
+
~doc:"RFC 6901 JSON Pointer"
643
+
~dec ~enc ())
644
+
645
+
let jsont_kind : [ `Nav of nav t | `Append of append t ] Jsont.t =
646
+
let dec _meta s = of_string_kind s in
613
647
let enc = function
614
648
| `Nav p -> to_string p
615
649
| `Append p -> to_string p
616
650
in
617
651
Jsont.Base.string (Jsont.Base.map
618
-
~kind:"JSON Pointer"
619
-
~doc:"RFC 6901 JSON Pointer"
652
+
~kind:"JSON Pointer (kind)"
653
+
~doc:"RFC 6901 JSON Pointer with kind tag"
620
654
~dec ~enc ())
621
655
622
656
let jsont_nav : nav t Jsont.t =
···
627
661
~doc:"RFC 6901 JSON Pointer (navigation only)"
628
662
~dec ~enc ())
629
663
630
-
let jsont_uri_fragment : [ `Nav of nav t | `Append of append t ] Jsont.t =
664
+
let jsont_uri_fragment : any Jsont.t =
631
665
let dec _meta s = of_uri_fragment s in
632
-
let enc = function
633
-
| `Nav p -> to_uri_fragment p
634
-
| `Append p -> to_uri_fragment p
635
-
in
666
+
let enc (Any p) = to_uri_fragment p in
636
667
Jsont.Base.string (Jsont.Base.map
637
668
~kind:"JSON Pointer (URI fragment)"
638
669
~doc:"RFC 6901 JSON Pointer in URI fragment encoding"
···
657
688
Jsont.map Jsont.json ~dec ~enc:(fun _ ->
658
689
Jsont.Error.msgf Jsont.Meta.none "path: encode not supported")
659
690
660
-
let set_path (type a) ?(allow_absent = false) t (p : a t) v =
691
+
let set_path ?(allow_absent = false) t (p : any) v =
661
692
let encoded = match Jsont.Json.encode' t v with
662
693
| Ok json -> json
663
694
| Error e -> raise (Jsont.Error e)
···
692
723
| Ok j -> j
693
724
| Error e -> raise (Jsont.Error e)
694
725
in
695
-
set p json ~value:re_encoded
726
+
set (Any p) json ~value:re_encoded
696
727
in
697
728
Jsont.map Jsont.json ~dec ~enc:(fun j -> j)
698
729
+71
-16
src/jsont_pointer.mli
+71
-16
src/jsont_pointer.mli
···
135
135
(** Phantom type for pointers ending with [-] (the "after last element"
136
136
position). These can only be used with {!add} and {!set}. *)
137
137
138
+
(** {2 Existential wrapper}
139
+
140
+
The {!any} type wraps a pointer of unknown phantom type, allowing
141
+
ergonomic use with mutation operations like {!set} and {!add} without
142
+
needing to pattern match on the pointer kind. *)
143
+
144
+
type any = Any : _ t -> any
145
+
(** Existential wrapper for pointers. Use this when you don't need to
146
+
distinguish between navigation and append pointers at the type level,
147
+
such as when using {!set} or {!add} which accept either kind. *)
148
+
138
149
val root : nav t
139
150
(** [root] is the empty pointer that references the whole document.
140
151
In string form this is [""]. *)
···
174
185
Note: for append pointers, this returns the indices of the path
175
186
portion; the [-] (append position) is not represented as an index. *)
176
187
188
+
(** {2:coercion Coercion and inspection} *)
189
+
190
+
val any : _ t -> any
191
+
(** [any p] wraps a typed pointer in the existential {!any} type.
192
+
Use this when you have a [nav t] or [append t] but need an {!any}
193
+
for use with functions like {!set} or {!add}. *)
194
+
195
+
val is_nav : any -> bool
196
+
(** [is_nav p] is [true] if [p] is a navigation pointer (not an append
197
+
pointer ending with [-]). *)
198
+
199
+
val to_nav : any -> nav t option
200
+
(** [to_nav p] returns [Some nav_p] if [p] is a navigation pointer,
201
+
or [None] if it's an append pointer. *)
202
+
203
+
val to_nav_exn : any -> nav t
204
+
(** [to_nav_exn p] returns the navigation pointer if [p] is one.
205
+
@raise Jsont.Error if [p] is an append pointer. *)
206
+
177
207
(** {2:parsing Parsing} *)
178
208
179
-
val of_string : string -> [ `Nav of nav t | `Append of append t ]
209
+
val of_string : string -> any
180
210
(** [of_string s] parses a JSON Pointer from its string representation.
181
211
182
-
Returns [`Nav p] for pointers without [-], or [`Append p] for
183
-
pointers ending with [-].
212
+
Returns an {!any} pointer that can be used directly with mutation
213
+
operations like {!set} and {!add}. For retrieval operations like
214
+
{!get}, use {!of_string_nav} instead.
184
215
185
216
The string must be either empty (representing the root) or start
186
217
with [/]. Each segment between [/] characters is unescaped as a
···
191
222
- Invalid escape sequence ([~] not followed by [0] or [1])
192
223
- [-] appears in non-final position *)
193
224
225
+
val of_string_kind : string -> [ `Nav of nav t | `Append of append t ]
226
+
(** [of_string_kind s] parses a JSON Pointer and returns a tagged variant
227
+
indicating whether it's a navigation or append pointer.
228
+
229
+
Use this when you need to handle navigation and append pointers
230
+
differently, or when you need a typed pointer for operations that
231
+
require a specific kind.
232
+
233
+
@raise Jsont.Error if [s] has invalid syntax. *)
234
+
194
235
val of_string_nav : string -> nav t
195
236
(** [of_string_nav s] parses a JSON Pointer that must not contain [-].
237
+
238
+
Use this when you need a {!nav} pointer for retrieval operations
239
+
like {!get} or {!find}.
196
240
197
241
@raise Jsont.Error if [s] has invalid syntax or contains [-]. *)
198
242
199
-
val of_string_result : string -> ([ `Nav of nav t | `Append of append t ], string) result
243
+
val of_string_result : string -> (any, string) result
200
244
(** [of_string_result s] is like {!of_string} but returns a result
201
245
instead of raising. *)
202
246
203
-
val of_uri_fragment : string -> [ `Nav of nav t | `Append of append t ]
247
+
val of_uri_fragment : string -> any
204
248
(** [of_uri_fragment s] parses a JSON Pointer from URI fragment form.
205
249
206
250
This is like {!of_string} but first percent-decodes the string
···
215
259
216
260
@raise Jsont.Error if invalid or contains [-]. *)
217
261
218
-
val of_uri_fragment_result : string -> ([ `Nav of nav t | `Append of append t ], string) result
262
+
val of_uri_fragment_result : string -> (any, string) result
219
263
(** [of_uri_fragment_result s] is like {!of_uri_fragment} but returns
220
264
a result instead of raising. *)
221
265
···
296
340
All mutation functions return a new JSON value with the modification
297
341
applied; they do not mutate the input.
298
342
299
-
Functions that support the [-] token ({!add}, {!set}) accept any
300
-
pointer type ([_ t]). Functions that require an existing element
301
-
({!remove}, {!replace}) only accept {!nav} pointers. *)
343
+
Functions that support the [-] token ({!set}, {!add}, {!move}, {!copy})
344
+
accept {!any} pointers, making them easy to use with {!of_string}.
345
+
Functions that require an existing element ({!remove}, {!replace})
346
+
only accept {!nav} pointers. *)
302
347
303
-
val set : _ t -> Jsont.json -> value:Jsont.json -> Jsont.json
348
+
val set : any -> Jsont.json -> value:Jsont.json -> Jsont.json
304
349
(** [set p json ~value] replaces the value at pointer [p] with [value].
305
350
306
351
For {!append} pointers, appends [value] to the end of the array.
307
352
353
+
This accepts {!any} pointers directly from {!of_string}:
354
+
{[set (of_string "/tasks/-") json ~value:(Jsont.Json.string "new task")]}
355
+
308
356
@raise Jsont.Error if the pointer doesn't resolve to an existing
309
357
location (except for {!append} pointers on arrays). *)
310
358
311
-
val add : _ t -> Jsont.json -> value:Jsont.json -> Jsont.json
359
+
val add : any -> Jsont.json -> value:Jsont.json -> Jsont.json
312
360
(** [add p json ~value] adds [value] at the location specified by [p].
313
361
314
362
The behavior depends on the target:
···
342
390
343
391
@raise Jsont.Error if the pointer doesn't resolve to an existing value. *)
344
392
345
-
val move : from:nav t -> path:_ t -> Jsont.json -> Jsont.json
393
+
val move : from:nav t -> path:any -> Jsont.json -> Jsont.json
346
394
(** [move ~from ~path json] moves the value from [from] to [path].
347
395
348
396
This is equivalent to {!remove} at [from] followed by {!add}
···
352
400
- [from] doesn't resolve to a value
353
401
- [path] is a proper prefix of [from] (would create a cycle) *)
354
402
355
-
val copy : from:nav t -> path:_ t -> Jsont.json -> Jsont.json
403
+
val copy : from:nav t -> path:any -> Jsont.json -> Jsont.json
356
404
(** [copy ~from ~path json] copies the value from [from] to [path].
357
405
358
406
This is equivalent to {!get} at [from] followed by {!add}
···
374
422
These types and functions integrate JSON Pointers with the {!Jsont}
375
423
codec system. *)
376
424
377
-
val jsont : [ `Nav of nav t | `Append of append t ] Jsont.t
425
+
val jsont : any Jsont.t
378
426
(** [jsont] is a {!Jsont.t} codec for JSON Pointers.
379
427
380
428
On decode, parses a JSON string as a JSON Pointer using {!of_string}.
381
429
On encode, serializes a pointer to a JSON string using {!to_string}. *)
382
430
431
+
val jsont_kind : [ `Nav of nav t | `Append of append t ] Jsont.t
432
+
(** [jsont_kind] is a {!Jsont.t} codec for JSON Pointers that preserves
433
+
the pointer kind in the type.
434
+
435
+
On decode, parses using {!of_string_kind}.
436
+
On encode, serializes using {!to_string}. *)
437
+
383
438
val jsont_nav : nav t Jsont.t
384
439
(** [jsont_nav] is a {!Jsont.t} codec for navigation JSON Pointers.
385
440
386
441
On decode, parses using {!of_string_nav} (fails on [-]).
387
442
On encode, serializes using {!to_string}. *)
388
443
389
-
val jsont_uri_fragment : [ `Nav of nav t | `Append of append t ] Jsont.t
444
+
val jsont_uri_fragment : any Jsont.t
390
445
(** [jsont_uri_fragment] is like {!jsont} but uses URI fragment encoding.
391
446
392
447
On decode, parses using {!of_uri_fragment}.
···
405
460
406
461
This is similar to {!Jsont.path} but uses JSON Pointer syntax. *)
407
462
408
-
val set_path : ?allow_absent:bool -> 'a Jsont.t -> _ t -> 'a -> Jsont.json Jsont.t
463
+
val set_path : ?allow_absent:bool -> 'a Jsont.t -> any -> 'a -> Jsont.json Jsont.t
409
464
(** [set_path t p v] sets the value at pointer [p] to [v] encoded with [t].
410
465
411
466
If [allow_absent] is [true] (default [false]), creates missing
+485
test/api.t
+485
test/api.t
···
1
+
JSON Pointer API Test Suite
2
+
3
+
This tests all functions exposed in jsont_pointer.mli.
4
+
5
+
================================================================================
6
+
Index Functions (mem, nth, pp_index, equal_index, compare_index)
7
+
================================================================================
8
+
9
+
Creating indices with mem:
10
+
$ ./test_pointer.exe mem "foo"
11
+
mem(foo) = /foo
12
+
$ ./test_pointer.exe mem ""
13
+
mem() = /
14
+
$ ./test_pointer.exe mem "a/b"
15
+
mem(a/b) = /a~1b
16
+
$ ./test_pointer.exe mem "m~n"
17
+
mem(m~n) = /m~0n
18
+
19
+
Creating indices with nth:
20
+
$ ./test_pointer.exe nth 0
21
+
nth(0) = /0
22
+
$ ./test_pointer.exe nth 5
23
+
nth(5) = /5
24
+
$ ./test_pointer.exe nth 123
25
+
nth(123) = /123
26
+
27
+
Testing index equality:
28
+
$ ./test_pointer.exe equal-index "foo" "foo"
29
+
true
30
+
$ ./test_pointer.exe equal-index "foo" "bar"
31
+
false
32
+
$ ./test_pointer.exe equal-index "0" "0"
33
+
true
34
+
$ ./test_pointer.exe equal-index "0" "1"
35
+
false
36
+
$ ./test_pointer.exe equal-index "foo" "0"
37
+
false
38
+
39
+
Testing index comparison:
40
+
$ ./test_pointer.exe compare-index "foo" "foo"
41
+
EQ
42
+
$ ./test_pointer.exe compare-index "aaa" "bbb"
43
+
LT
44
+
$ ./test_pointer.exe compare-index "bbb" "aaa"
45
+
GT
46
+
$ ./test_pointer.exe compare-index "0" "0"
47
+
EQ
48
+
$ ./test_pointer.exe compare-index "0" "5"
49
+
LT
50
+
$ ./test_pointer.exe compare-index "5" "0"
51
+
GT
52
+
$ ./test_pointer.exe compare-index "foo" "0"
53
+
LT
54
+
$ ./test_pointer.exe compare-index "0" "foo"
55
+
GT
56
+
57
+
================================================================================
58
+
Pointer Constructors (root, is_root, make, /, append_index, at_end, concat, parent, last)
59
+
================================================================================
60
+
61
+
Root pointer:
62
+
$ ./test_pointer.exe root
63
+
root =
64
+
is_root(root) = true
65
+
66
+
Testing is_root:
67
+
$ ./test_pointer.exe is-root ""
68
+
true
69
+
$ ./test_pointer.exe is-root "/foo"
70
+
false
71
+
$ ./test_pointer.exe is-root "/foo/bar"
72
+
false
73
+
74
+
Making pointers from indices:
75
+
$ ./test_pointer.exe make "foo"
76
+
/foo
77
+
$ ./test_pointer.exe make "foo,bar"
78
+
/foo/bar
79
+
$ ./test_pointer.exe make "foo,0,bar"
80
+
/foo/0/bar
81
+
$ ./test_pointer.exe make "0,1,2"
82
+
/0/1/2
83
+
84
+
Appending index to pointer:
85
+
$ ./test_pointer.exe append-index "" "foo"
86
+
/foo
87
+
$ ./test_pointer.exe append-index "/foo" "bar"
88
+
/foo/bar
89
+
$ ./test_pointer.exe append-index "/foo" "0"
90
+
/foo/0
91
+
$ ./test_pointer.exe append-index "/foo/bar" "baz"
92
+
/foo/bar/baz
93
+
94
+
Creating append pointers with at_end:
95
+
$ ./test_pointer.exe at-end ""
96
+
/-
97
+
$ ./test_pointer.exe at-end "/foo"
98
+
/foo/-
99
+
$ ./test_pointer.exe at-end "/foo/bar"
100
+
/foo/bar/-
101
+
102
+
Concatenating pointers:
103
+
$ ./test_pointer.exe concat "" ""
104
+
105
+
106
+
$ ./test_pointer.exe concat "/foo" ""
107
+
/foo
108
+
$ ./test_pointer.exe concat "" "/bar"
109
+
/bar
110
+
$ ./test_pointer.exe concat "/foo" "/bar"
111
+
/foo/bar
112
+
$ ./test_pointer.exe concat "/a/b" "/c/d"
113
+
/a/b/c/d
114
+
115
+
Getting parent pointer:
116
+
$ ./test_pointer.exe parent ""
117
+
None
118
+
$ ./test_pointer.exe parent "/foo"
119
+
Some()
120
+
$ ./test_pointer.exe parent "/foo/bar"
121
+
Some(/foo)
122
+
$ ./test_pointer.exe parent "/a/b/c"
123
+
Some(/a/b)
124
+
125
+
Getting last index:
126
+
$ ./test_pointer.exe last ""
127
+
None
128
+
$ ./test_pointer.exe last "/foo"
129
+
Some(/foo)
130
+
$ ./test_pointer.exe last "/foo/bar"
131
+
Some(/bar)
132
+
$ ./test_pointer.exe last "/foo/0"
133
+
Some(/0)
134
+
135
+
Getting all indices:
136
+
$ ./test_pointer.exe indices ""
137
+
[]
138
+
$ ./test_pointer.exe indices "/foo"
139
+
[Mem:foo]
140
+
$ ./test_pointer.exe indices "/foo/bar"
141
+
[Mem:foo, Mem:bar]
142
+
$ ./test_pointer.exe indices "/foo/0/bar"
143
+
[Mem:foo, Nth:0, Mem:bar]
144
+
$ ./test_pointer.exe indices "/foo/-"
145
+
[Mem:foo]
146
+
147
+
================================================================================
148
+
Coercion Functions (any, is_nav, to_nav, to_nav_exn)
149
+
================================================================================
150
+
151
+
Note: is_nav is tested indirectly via the parse tests. Testing to_nav:
152
+
153
+
$ ./test_pointer.exe to-nav ""
154
+
Some()
155
+
$ ./test_pointer.exe to-nav "/foo"
156
+
Some(/foo)
157
+
$ ./test_pointer.exe to-nav "/foo/bar"
158
+
Some(/foo/bar)
159
+
$ ./test_pointer.exe to-nav "/-"
160
+
None
161
+
$ ./test_pointer.exe to-nav "/foo/-"
162
+
None
163
+
164
+
Testing to_nav_exn:
165
+
$ ./test_pointer.exe to-nav-exn ""
166
+
OK:
167
+
$ ./test_pointer.exe to-nav-exn "/foo"
168
+
OK: /foo
169
+
$ ./test_pointer.exe to-nav-exn "/-"
170
+
ERROR: JSON Pointer: cannot convert append pointer to nav pointer
171
+
$ ./test_pointer.exe to-nav-exn "/foo/-"
172
+
ERROR: JSON Pointer: cannot convert append pointer to nav pointer
173
+
174
+
================================================================================
175
+
Parsing Functions (of_string_kind, of_string_result, of_uri_fragment_nav, of_uri_fragment_result)
176
+
================================================================================
177
+
178
+
Testing of_string_kind:
179
+
$ ./test_pointer.exe of-string-kind ""
180
+
Nav()
181
+
$ ./test_pointer.exe of-string-kind "/foo"
182
+
Nav(/foo)
183
+
$ ./test_pointer.exe of-string-kind "/-"
184
+
Append(/-)
185
+
$ ./test_pointer.exe of-string-kind "/foo/-"
186
+
Append(/foo/-)
187
+
$ ./test_pointer.exe of-string-kind "/foo/bar"
188
+
Nav(/foo/bar)
189
+
190
+
Testing of_string_result:
191
+
$ ./test_pointer.exe of-string-result ""
192
+
Ok()
193
+
$ ./test_pointer.exe of-string-result "/foo"
194
+
Ok(/foo)
195
+
$ ./test_pointer.exe of-string-result "/-"
196
+
Ok(/-)
197
+
$ ./test_pointer.exe of-string-result "invalid"
198
+
Error(Invalid JSON Pointer: must be empty or start with '/': invalid)
199
+
$ ./test_pointer.exe of-string-result "/~"
200
+
Error(Invalid JSON Pointer: incomplete escape sequence at end)
201
+
202
+
Testing of_uri_fragment_nav:
203
+
$ ./test_pointer.exe of-uri-fragment-nav ""
204
+
OK:
205
+
$ ./test_pointer.exe of-uri-fragment-nav "/foo"
206
+
OK: /foo
207
+
$ ./test_pointer.exe of-uri-fragment-nav "/foo/bar"
208
+
OK: /foo/bar
209
+
$ ./test_pointer.exe of-uri-fragment-nav "/foo%2Fbar"
210
+
OK: /foo/bar
211
+
$ ./test_pointer.exe of-uri-fragment-nav "/-"
212
+
ERROR: Invalid JSON Pointer: '-' not allowed in navigation pointer
213
+
214
+
Testing of_uri_fragment_result:
215
+
$ ./test_pointer.exe of-uri-fragment-result ""
216
+
Ok()
217
+
$ ./test_pointer.exe of-uri-fragment-result "/foo"
218
+
Ok(/foo)
219
+
$ ./test_pointer.exe of-uri-fragment-result "/-"
220
+
Ok(/-)
221
+
$ ./test_pointer.exe of-uri-fragment-result "invalid"
222
+
Error(Invalid JSON Pointer: must be empty or start with '/': invalid)
223
+
224
+
================================================================================
225
+
Pretty Printing (pp, pp_verbose)
226
+
================================================================================
227
+
228
+
Testing pp:
229
+
$ ./test_pointer.exe pp ""
230
+
231
+
232
+
$ ./test_pointer.exe pp "/foo"
233
+
/foo
234
+
$ ./test_pointer.exe pp "/foo/bar"
235
+
/foo/bar
236
+
$ ./test_pointer.exe pp "/-"
237
+
/-
238
+
$ ./test_pointer.exe pp "/foo/-"
239
+
/foo/-
240
+
241
+
Testing pp_verbose:
242
+
$ ./test_pointer.exe pp-verbose ""
243
+
[]
244
+
$ ./test_pointer.exe pp-verbose "/foo"
245
+
[Mem "foo"]
246
+
$ ./test_pointer.exe pp-verbose "/foo/0"
247
+
[Mem "foo"; Nth 0]
248
+
$ ./test_pointer.exe pp-verbose "/foo/bar/baz"
249
+
[Mem "foo"; Mem "bar"; Mem "baz"]
250
+
$ ./test_pointer.exe pp-verbose "/-"
251
+
[] /-
252
+
$ ./test_pointer.exe pp-verbose "/foo/-"
253
+
[Mem "foo"] /-
254
+
255
+
================================================================================
256
+
Comparison (equal, compare)
257
+
================================================================================
258
+
259
+
Testing equal:
260
+
$ ./test_pointer.exe equal "" ""
261
+
true
262
+
$ ./test_pointer.exe equal "/foo" "/foo"
263
+
true
264
+
$ ./test_pointer.exe equal "/foo" "/bar"
265
+
false
266
+
$ ./test_pointer.exe equal "/foo/0" "/foo/0"
267
+
true
268
+
$ ./test_pointer.exe equal "/foo/0" "/foo/1"
269
+
false
270
+
$ ./test_pointer.exe equal "/-" "/-"
271
+
true
272
+
$ ./test_pointer.exe equal "/foo/-" "/foo/-"
273
+
true
274
+
$ ./test_pointer.exe equal "/foo" "/foo/-"
275
+
false
276
+
277
+
Testing compare:
278
+
$ ./test_pointer.exe compare "" ""
279
+
EQ
280
+
$ ./test_pointer.exe compare "/foo" "/foo"
281
+
EQ
282
+
$ ./test_pointer.exe compare "/aaa" "/bbb"
283
+
LT
284
+
$ ./test_pointer.exe compare "/bbb" "/aaa"
285
+
GT
286
+
$ ./test_pointer.exe compare "/foo" "/foo/bar"
287
+
LT
288
+
$ ./test_pointer.exe compare "/foo/bar" "/foo"
289
+
GT
290
+
$ ./test_pointer.exe compare "/foo" "/foo/-"
291
+
LT
292
+
$ ./test_pointer.exe compare "/foo/-" "/foo"
293
+
GT
294
+
295
+
================================================================================
296
+
Jsont.Path Conversion (of_path, to_path)
297
+
================================================================================
298
+
299
+
Testing of_path (builds a path programmatically):
300
+
$ ./test_pointer.exe of-path
301
+
/0/foo/1
302
+
303
+
Testing to_path:
304
+
$ ./test_pointer.exe to-path ""
305
+
[]
306
+
$ ./test_pointer.exe to-path "/foo"
307
+
[Mem:foo]
308
+
$ ./test_pointer.exe to-path "/foo/0"
309
+
[Mem:foo, Nth:0]
310
+
$ ./test_pointer.exe to-path "/a/b/c"
311
+
[Mem:a, Mem:b, Mem:c]
312
+
313
+
================================================================================
314
+
Evaluation (get_result, set)
315
+
================================================================================
316
+
317
+
Testing get_result:
318
+
$ ./test_pointer.exe get-result '{"foo":"bar"}' '/foo'
319
+
Ok("bar")
320
+
$ ./test_pointer.exe get-result '{"foo":{"bar":"baz"}}' '/foo/bar'
321
+
Ok("baz")
322
+
$ ./test_pointer.exe get-result '["a","b","c"]' '/1'
323
+
Ok("b")
324
+
$ ./test_pointer.exe get-result '{"foo":"bar"}' '/baz'
325
+
Error(JSON Pointer: member 'baz' not found
326
+
File "-":)
327
+
$ ./test_pointer.exe get-result '["a","b"]' '/5'
328
+
Error(JSON Pointer: index 5 out of bounds (array has 2 elements)
329
+
File "-":)
330
+
331
+
Testing set (with nav pointer):
332
+
$ ./test_pointer.exe set '{"foo":"bar"}' '/foo' '"baz"'
333
+
{"foo":"baz"}
334
+
$ ./test_pointer.exe set '{"foo":{"bar":"old"}}' '/foo/bar' '"new"'
335
+
{"foo":{"bar":"new"}}
336
+
$ ./test_pointer.exe set '["a","b","c"]' '/1' '"x"'
337
+
["a","x","c"]
338
+
339
+
Testing set (with append pointer):
340
+
$ ./test_pointer.exe set '["a","b"]' '/-' '"c"'
341
+
["a","b","c"]
342
+
$ ./test_pointer.exe set '{"arr":["x"]}' '/arr/-' '"y"'
343
+
{"arr":["x","y"]}
344
+
345
+
================================================================================
346
+
Jsont Codecs (jsont, jsont_kind, jsont_nav, jsont_uri_fragment)
347
+
================================================================================
348
+
349
+
Testing jsont codec roundtrip:
350
+
$ ./test_pointer.exe jsont-codec ""
351
+
""
352
+
$ ./test_pointer.exe jsont-codec "/foo"
353
+
"/foo"
354
+
$ ./test_pointer.exe jsont-codec "/foo/bar"
355
+
"/foo/bar"
356
+
$ ./test_pointer.exe jsont-codec "/-"
357
+
"/-"
358
+
$ ./test_pointer.exe jsont-codec "/foo/-"
359
+
"/foo/-"
360
+
361
+
Testing jsont_kind codec:
362
+
$ ./test_pointer.exe jsont-kind ""
363
+
Nav()
364
+
$ ./test_pointer.exe jsont-kind "/foo"
365
+
Nav(/foo)
366
+
$ ./test_pointer.exe jsont-kind "/-"
367
+
Append(/-)
368
+
$ ./test_pointer.exe jsont-kind "/foo/-"
369
+
Append(/foo/-)
370
+
371
+
Testing jsont_nav codec:
372
+
$ ./test_pointer.exe jsont-nav ""
373
+
OK:
374
+
$ ./test_pointer.exe jsont-nav "/foo"
375
+
OK: /foo
376
+
$ ./test_pointer.exe jsont-nav "/-"
377
+
ERROR: Invalid JSON Pointer: '-' not allowed in navigation pointer
378
+
$ ./test_pointer.exe jsont-nav "/foo/-"
379
+
ERROR: Invalid JSON Pointer: '-' not allowed in navigation pointer
380
+
381
+
Testing jsont_uri_fragment codec:
382
+
$ ./test_pointer.exe jsont-uri-fragment ""
383
+
""
384
+
$ ./test_pointer.exe jsont-uri-fragment "/foo"
385
+
"/foo"
386
+
$ ./test_pointer.exe jsont-uri-fragment "/a~1b"
387
+
"/a~1b"
388
+
389
+
================================================================================
390
+
Query Combinators (path, set_path, update_path, delete_path)
391
+
================================================================================
392
+
393
+
Testing path combinator:
394
+
$ ./test_pointer.exe query-path '{"name":"alice"}' '/name'
395
+
OK: alice
396
+
$ ./test_pointer.exe query-path '{"user":{"name":"bob"}}' '/user/name'
397
+
OK: bob
398
+
399
+
Testing path combinator with absent:
400
+
$ ./test_pointer.exe query-path-absent '{"name":"alice"}' '/name' 'default'
401
+
OK: alice
402
+
$ ./test_pointer.exe query-path-absent '{"other":"value"}' '/name' 'default'
403
+
OK: default
404
+
405
+
Testing set_path combinator:
406
+
$ ./test_pointer.exe set-path '{"name":"alice"}' '/name' 'bob'
407
+
{"name":"bob"}
408
+
$ ./test_pointer.exe set-path '{"user":{"name":"old"}}' '/user/name' 'new'
409
+
{"user":{"name":"new"}}
410
+
411
+
Testing update_path combinator:
412
+
$ ./test_pointer.exe update-path '{"name":"alice"}' '/name'
413
+
{"name":"alice"}
414
+
$ ./test_pointer.exe update-path '{"user":{"name":"bob"}}' '/user/name'
415
+
{"user":{"name":"bob"}}
416
+
417
+
Testing delete_path combinator:
418
+
$ ./test_pointer.exe delete-path '{"foo":"bar","baz":"qux"}' '/foo'
419
+
{"baz":"qux"}
420
+
$ ./test_pointer.exe delete-path '{"user":{"name":"bob","age":30}}' '/user/name'
421
+
{"user":{"age":30}}
422
+
423
+
Testing delete_path with allow_absent:
424
+
$ ./test_pointer.exe delete-path-absent '{"foo":"bar"}' '/baz'
425
+
{"foo":"bar"}
426
+
$ ./test_pointer.exe delete-path-absent '{"foo":"bar"}' '/foo'
427
+
{}
428
+
429
+
================================================================================
430
+
JMAP Extended Pointers - Additional Tests
431
+
================================================================================
432
+
433
+
Testing Jmap.of_string_result:
434
+
$ ./test_pointer.exe jmap-of-string-result ""
435
+
Ok()
436
+
$ ./test_pointer.exe jmap-of-string-result "/foo"
437
+
Ok(/foo)
438
+
$ ./test_pointer.exe jmap-of-string-result "/*"
439
+
Ok(/*)
440
+
$ ./test_pointer.exe jmap-of-string-result "/list/*/id"
441
+
Ok(/list/*/id)
442
+
$ ./test_pointer.exe jmap-of-string-result "invalid"
443
+
Error(Invalid JMAP Pointer: must be empty or start with '/': invalid)
444
+
$ ./test_pointer.exe jmap-of-string-result "/-"
445
+
Error(Invalid JMAP Pointer: '-' not supported in result reference paths)
446
+
447
+
Testing Jmap.pp:
448
+
$ ./test_pointer.exe jmap-pp ""
449
+
450
+
451
+
$ ./test_pointer.exe jmap-pp "/foo"
452
+
/foo
453
+
$ ./test_pointer.exe jmap-pp "/*"
454
+
/*
455
+
$ ./test_pointer.exe jmap-pp "/list/*/id"
456
+
/list/*/id
457
+
458
+
Testing Jmap.eval_result:
459
+
$ ./test_pointer.exe jmap-eval-result '{"foo":"bar"}' '/foo'
460
+
Ok("bar")
461
+
$ ./test_pointer.exe jmap-eval-result '{"list":[{"id":"a"},{"id":"b"}]}' '/list/*/id'
462
+
Ok(["a","b"])
463
+
$ ./test_pointer.exe jmap-eval-result '{"foo":"bar"}' '/baz'
464
+
Error(JMAP Pointer: member 'baz' not found
465
+
File "-":)
466
+
467
+
Testing Jmap.find:
468
+
$ ./test_pointer.exe jmap-find '{"foo":"bar"}' '/foo'
469
+
Some("bar")
470
+
$ ./test_pointer.exe jmap-find '{"list":[{"id":"a"},{"id":"b"}]}' '/list/*/id'
471
+
Some(["a","b"])
472
+
$ ./test_pointer.exe jmap-find '{"foo":"bar"}' '/baz'
473
+
None
474
+
$ ./test_pointer.exe jmap-find '{"obj":{"a":1}}' '/obj/*'
475
+
None
476
+
477
+
Testing Jmap.jsont codec:
478
+
$ ./test_pointer.exe jmap-jsont ""
479
+
""
480
+
$ ./test_pointer.exe jmap-jsont "/foo"
481
+
"/foo"
482
+
$ ./test_pointer.exe jmap-jsont "/*"
483
+
"/*"
484
+
$ ./test_pointer.exe jmap-jsont "/list/*/id"
485
+
"/list/*/id"
+629
-32
test/test_pointer.ml
+629
-32
test/test_pointer.ml
···
17
17
| Ok s -> s
18
18
| Error e -> failwith e
19
19
20
-
(* Helper to get indices from either nav or append pointer *)
21
-
let indices_of_result (result : [ `Nav of Jsont_pointer.nav Jsont_pointer.t
22
-
| `Append of Jsont_pointer.append Jsont_pointer.t ]) =
23
-
match result with
24
-
| `Nav p -> Jsont_pointer.indices p
25
-
| `Append p -> Jsont_pointer.indices p
20
+
(* Helper to get indices from any pointer *)
21
+
let indices_of_any (Jsont_pointer.Any p) = Jsont_pointer.indices p
22
+
23
+
(* Helper to convert to string from any pointer *)
24
+
let to_string_of_any (Jsont_pointer.Any p) = Jsont_pointer.to_string p
26
25
27
-
(* Helper to convert to string from either nav or append pointer *)
28
-
let to_string_of_result (result : [ `Nav of Jsont_pointer.nav Jsont_pointer.t
29
-
| `Append of Jsont_pointer.append Jsont_pointer.t ]) =
30
-
match result with
31
-
| `Nav p -> Jsont_pointer.to_string p
32
-
| `Append p -> Jsont_pointer.to_string p
26
+
(* Helper to check if pointer is append *)
27
+
let is_append_any p = not (Jsont_pointer.is_nav p)
33
28
34
29
(* Test: parse pointer and print indices *)
35
30
let test_parse pointer_str =
36
31
try
37
32
let result = Jsont_pointer.of_string pointer_str in
38
-
let indices = indices_of_result result in
33
+
let indices = indices_of_any result in
39
34
let index_strs = List.map (fun idx ->
40
35
match idx with
41
36
| Jsont.Path.Mem (s, _) -> Printf.sprintf "Mem:%s" s
42
37
| Jsont.Path.Nth (n, _) -> Printf.sprintf "Nth:%d" n
43
38
) indices in
44
-
let suffix = match result with `Nav _ -> "" | `Append _ -> ", /-" in
39
+
let suffix = if is_append_any result then ", /-" else "" in
45
40
Printf.printf "OK: [%s%s]\n" (String.concat ", " index_strs) suffix
46
41
with Jsont.Error e ->
47
42
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
50
45
let test_roundtrip pointer_str =
51
46
try
52
47
let result = Jsont_pointer.of_string pointer_str in
53
-
let s = to_string_of_result result in
48
+
let s = to_string_of_any result in
54
49
if s = pointer_str then
55
50
Printf.printf "OK: %s\n" s
56
51
else
···
88
83
let test_uri_fragment pointer_str =
89
84
try
90
85
let result = Jsont_pointer.of_string pointer_str in
91
-
let frag = match result with
92
-
| `Nav p -> Jsont_pointer.to_uri_fragment p
93
-
| `Append p -> Jsont_pointer.to_uri_fragment p
94
-
in
86
+
let (Jsont_pointer.Any p) = result in
87
+
let frag = Jsont_pointer.to_uri_fragment p in
95
88
let result2 = Jsont_pointer.of_uri_fragment frag in
96
-
let s2 = to_string_of_result result2 in
89
+
let s2 = to_string_of_any result2 in
97
90
if s2 = pointer_str then
98
91
Printf.printf "OK: %s -> %s\n" pointer_str frag
99
92
else
···
106
99
try
107
100
let json = parse_json json_str in
108
101
let value = parse_json value_str in
109
-
let result = match Jsont_pointer.of_string pointer_str with
110
-
| `Nav p -> Jsont_pointer.add p json ~value
111
-
| `Append p -> Jsont_pointer.add p json ~value
112
-
in
102
+
let p = Jsont_pointer.of_string pointer_str in
103
+
let result = Jsont_pointer.add p json ~value in
113
104
Printf.printf "%s\n" (json_to_string result)
114
105
with Jsont.Error e ->
115
106
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
140
131
try
141
132
let json = parse_json json_str in
142
133
let from = Jsont_pointer.of_string_nav from_str in
143
-
let result = match Jsont_pointer.of_string path_str with
144
-
| `Nav path -> Jsont_pointer.move ~from ~path json
145
-
| `Append path -> Jsont_pointer.move ~from ~path json
146
-
in
134
+
let path = Jsont_pointer.of_string path_str in
135
+
let result = Jsont_pointer.move ~from ~path json in
147
136
Printf.printf "%s\n" (json_to_string result)
148
137
with Jsont.Error e ->
149
138
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
153
142
try
154
143
let json = parse_json json_str in
155
144
let from = Jsont_pointer.of_string_nav from_str in
156
-
let result = match Jsont_pointer.of_string path_str with
157
-
| `Nav path -> Jsont_pointer.copy ~from ~path json
158
-
| `Append path -> Jsont_pointer.copy ~from ~path json
159
-
in
145
+
let path = Jsont_pointer.of_string path_str in
146
+
let result = Jsont_pointer.copy ~from ~path json in
160
147
Printf.printf "%s\n" (json_to_string result)
161
148
with Jsont.Error e ->
162
149
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
···
286
273
| Failure e ->
287
274
Printf.printf "FAIL: %s\n" e
288
275
276
+
(* Test: index functions - mem, nth, pp_index, equal_index, compare_index *)
277
+
let test_mem name =
278
+
let idx = Jsont_pointer.mem name in
279
+
Format.printf "mem(%s) = %a\n" name Jsont_pointer.pp_index idx
280
+
281
+
let test_nth n =
282
+
let idx = Jsont_pointer.nth n in
283
+
Format.printf "nth(%d) = %a\n" n Jsont_pointer.pp_index idx
284
+
285
+
let test_equal_index idx1_str idx2_str =
286
+
let parse_idx s =
287
+
if String.length s > 0 && s.[0] >= '0' && s.[0] <= '9' then
288
+
Jsont_pointer.nth (int_of_string s)
289
+
else
290
+
Jsont_pointer.mem s
291
+
in
292
+
let idx1 = parse_idx idx1_str in
293
+
let idx2 = parse_idx idx2_str in
294
+
Printf.printf "%b\n" (Jsont_pointer.equal_index idx1 idx2)
295
+
296
+
let test_compare_index idx1_str idx2_str =
297
+
let parse_idx s =
298
+
if String.length s > 0 && s.[0] >= '0' && s.[0] <= '9' then
299
+
Jsont_pointer.nth (int_of_string s)
300
+
else
301
+
Jsont_pointer.mem s
302
+
in
303
+
let idx1 = parse_idx idx1_str in
304
+
let idx2 = parse_idx idx2_str in
305
+
let cmp = Jsont_pointer.compare_index idx1 idx2 in
306
+
if cmp < 0 then Printf.printf "LT\n"
307
+
else if cmp > 0 then Printf.printf "GT\n"
308
+
else Printf.printf "EQ\n"
309
+
310
+
(* Test: pointer constructors - root, is_root, make *)
311
+
let test_root () =
312
+
let r = Jsont_pointer.root in
313
+
Printf.printf "root = %s\n" (Jsont_pointer.to_string r);
314
+
Printf.printf "is_root(root) = %b\n" (Jsont_pointer.is_root r)
315
+
316
+
let test_is_root pointer_str =
317
+
try
318
+
let p = Jsont_pointer.of_string pointer_str in
319
+
let (Jsont_pointer.Any ptr) = p in
320
+
Printf.printf "%b\n" (Jsont_pointer.is_root ptr)
321
+
with Jsont.Error e ->
322
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
323
+
324
+
let test_make indices_str =
325
+
(* Parse comma-separated indices like "foo,0,bar" *)
326
+
let parts = String.split_on_char ',' indices_str in
327
+
let indices = List.map (fun s ->
328
+
let s = String.trim s in
329
+
if s = "" then Jsont_pointer.mem ""
330
+
else if String.length s > 0 && s.[0] >= '0' && s.[0] <= '9' then
331
+
Jsont_pointer.nth (int_of_string s)
332
+
else
333
+
Jsont_pointer.mem s
334
+
) parts in
335
+
let p = Jsont_pointer.make indices in
336
+
Printf.printf "%s\n" (Jsont_pointer.to_string p)
337
+
338
+
(* Test: append_index and / operator *)
339
+
let test_append_index base_str index_str =
340
+
try
341
+
let base = Jsont_pointer.of_string_nav base_str in
342
+
let idx =
343
+
if String.length index_str > 0 && index_str.[0] >= '0' && index_str.[0] <= '9' then
344
+
Jsont_pointer.nth (int_of_string index_str)
345
+
else
346
+
Jsont_pointer.mem index_str
347
+
in
348
+
let result = Jsont_pointer.(base / idx) in
349
+
Printf.printf "%s\n" (Jsont_pointer.to_string result)
350
+
with Jsont.Error e ->
351
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
352
+
353
+
(* Test: at_end *)
354
+
let test_at_end pointer_str =
355
+
try
356
+
let p = Jsont_pointer.of_string_nav pointer_str in
357
+
let append_p = Jsont_pointer.at_end p in
358
+
Printf.printf "%s\n" (Jsont_pointer.to_string append_p)
359
+
with Jsont.Error e ->
360
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
361
+
362
+
(* Test: concat *)
363
+
let test_concat p1_str p2_str =
364
+
try
365
+
let p1 = Jsont_pointer.of_string_nav p1_str in
366
+
let p2 = Jsont_pointer.of_string_nav p2_str in
367
+
let result = Jsont_pointer.concat p1 p2 in
368
+
Printf.printf "%s\n" (Jsont_pointer.to_string result)
369
+
with Jsont.Error e ->
370
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
371
+
372
+
(* Test: parent *)
373
+
let test_parent pointer_str =
374
+
try
375
+
let p = Jsont_pointer.of_string_nav pointer_str in
376
+
match Jsont_pointer.parent p with
377
+
| Some parent -> Printf.printf "Some(%s)\n" (Jsont_pointer.to_string parent)
378
+
| None -> Printf.printf "None\n"
379
+
with Jsont.Error e ->
380
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
381
+
382
+
(* Test: last *)
383
+
let test_last pointer_str =
384
+
try
385
+
let p = Jsont_pointer.of_string_nav pointer_str in
386
+
match Jsont_pointer.last p with
387
+
| Some idx -> Format.printf "Some(%a)\n" Jsont_pointer.pp_index idx
388
+
| None -> Printf.printf "None\n"
389
+
with Jsont.Error e ->
390
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
391
+
392
+
(* Test: indices *)
393
+
let test_indices pointer_str =
394
+
try
395
+
let p = Jsont_pointer.of_string pointer_str in
396
+
let indices = indices_of_any p in
397
+
let strs = List.map (fun idx ->
398
+
match idx with
399
+
| Jsont.Path.Mem (s, _) -> Printf.sprintf "Mem:%s" s
400
+
| Jsont.Path.Nth (n, _) -> Printf.sprintf "Nth:%d" n
401
+
) indices in
402
+
Printf.printf "[%s]\n" (String.concat ", " strs)
403
+
with Jsont.Error e ->
404
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
405
+
406
+
(* Test: coercion - to_nav, to_nav_exn *)
407
+
let test_to_nav pointer_str =
408
+
try
409
+
let p = Jsont_pointer.of_string pointer_str in
410
+
match Jsont_pointer.to_nav p with
411
+
| Some nav -> Printf.printf "Some(%s)\n" (Jsont_pointer.to_string nav)
412
+
| None -> Printf.printf "None\n"
413
+
with Jsont.Error e ->
414
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
415
+
416
+
let test_to_nav_exn pointer_str =
417
+
try
418
+
let p = Jsont_pointer.of_string pointer_str in
419
+
let nav = Jsont_pointer.to_nav_exn p in
420
+
Printf.printf "OK: %s\n" (Jsont_pointer.to_string nav)
421
+
with Jsont.Error e ->
422
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
423
+
424
+
(* Test: of_string_kind *)
425
+
let test_of_string_kind pointer_str =
426
+
try
427
+
match Jsont_pointer.of_string_kind pointer_str with
428
+
| `Nav p -> Printf.printf "Nav(%s)\n" (Jsont_pointer.to_string p)
429
+
| `Append p -> Printf.printf "Append(%s)\n" (Jsont_pointer.to_string p)
430
+
with Jsont.Error e ->
431
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
432
+
433
+
(* Test: of_string_result *)
434
+
let test_of_string_result pointer_str =
435
+
match Jsont_pointer.of_string_result pointer_str with
436
+
| Ok p -> Printf.printf "Ok(%s)\n" (to_string_of_any p)
437
+
| Error e -> Printf.printf "Error(%s)\n" e
438
+
439
+
(* Test: of_uri_fragment_nav *)
440
+
let test_of_uri_fragment_nav frag =
441
+
try
442
+
let p = Jsont_pointer.of_uri_fragment_nav frag in
443
+
Printf.printf "OK: %s\n" (Jsont_pointer.to_string p)
444
+
with Jsont.Error e ->
445
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
446
+
447
+
(* Test: of_uri_fragment_result *)
448
+
let test_of_uri_fragment_result frag =
449
+
match Jsont_pointer.of_uri_fragment_result frag with
450
+
| Ok p -> Printf.printf "Ok(%s)\n" (to_string_of_any p)
451
+
| Error e -> Printf.printf "Error(%s)\n" e
452
+
453
+
(* Test: pp and pp_verbose *)
454
+
let test_pp pointer_str =
455
+
try
456
+
let p = Jsont_pointer.of_string pointer_str in
457
+
let (Jsont_pointer.Any ptr) = p in
458
+
Format.printf "%a\n" Jsont_pointer.pp ptr
459
+
with Jsont.Error e ->
460
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
461
+
462
+
let test_pp_verbose pointer_str =
463
+
try
464
+
let p = Jsont_pointer.of_string pointer_str in
465
+
let (Jsont_pointer.Any ptr) = p in
466
+
Format.printf "%a\n" Jsont_pointer.pp_verbose ptr
467
+
with Jsont.Error e ->
468
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
469
+
470
+
(* Test: equal *)
471
+
let test_equal p1_str p2_str =
472
+
try
473
+
let p1 = Jsont_pointer.of_string p1_str in
474
+
let p2 = Jsont_pointer.of_string p2_str in
475
+
let (Jsont_pointer.Any ptr1) = p1 in
476
+
let (Jsont_pointer.Any ptr2) = p2 in
477
+
Printf.printf "%b\n" (Jsont_pointer.equal ptr1 ptr2)
478
+
with Jsont.Error e ->
479
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
480
+
481
+
(* Test: compare *)
482
+
let test_compare p1_str p2_str =
483
+
try
484
+
let p1 = Jsont_pointer.of_string p1_str in
485
+
let p2 = Jsont_pointer.of_string p2_str in
486
+
let (Jsont_pointer.Any ptr1) = p1 in
487
+
let (Jsont_pointer.Any ptr2) = p2 in
488
+
let cmp = Jsont_pointer.compare ptr1 ptr2 in
489
+
if cmp < 0 then Printf.printf "LT\n"
490
+
else if cmp > 0 then Printf.printf "GT\n"
491
+
else Printf.printf "EQ\n"
492
+
with Jsont.Error e ->
493
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
494
+
495
+
(* Test: of_path and to_path *)
496
+
let test_of_path () =
497
+
(* Create a Jsont.Path and convert to pointer *)
498
+
let path = Jsont.Path.(
499
+
root
500
+
|> Jsont.Path.nth 0
501
+
|> Jsont.Path.mem "foo"
502
+
|> Jsont.Path.nth 1
503
+
) in
504
+
let p = Jsont_pointer.of_path path in
505
+
Printf.printf "%s\n" (Jsont_pointer.to_string p)
506
+
507
+
let test_to_path pointer_str =
508
+
try
509
+
let p = Jsont_pointer.of_string_nav pointer_str in
510
+
let path = Jsont_pointer.to_path p in
511
+
(* Use rev_indices to get the indices in reverse order *)
512
+
let indices = Jsont.Path.rev_indices path in
513
+
let parts = List.rev_map (fun idx ->
514
+
match idx with
515
+
| Jsont.Path.Mem (s, _) -> Printf.sprintf "Mem:%s" s
516
+
| Jsont.Path.Nth (n, _) -> Printf.sprintf "Nth:%d" n
517
+
) indices in
518
+
Printf.printf "[%s]\n" (String.concat ", " parts)
519
+
with Jsont.Error e ->
520
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
521
+
522
+
(* Test: get_result *)
523
+
let test_get_result json_str pointer_str =
524
+
try
525
+
let json = parse_json json_str in
526
+
let p = Jsont_pointer.of_string_nav pointer_str in
527
+
match Jsont_pointer.get_result p json with
528
+
| Ok result -> Printf.printf "Ok(%s)\n" (json_to_string result)
529
+
| Error e -> Printf.printf "Error(%s)\n" (Jsont.Error.to_string e)
530
+
with Jsont.Error e ->
531
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
532
+
533
+
(* Test: set operation *)
534
+
let test_set json_str pointer_str value_str =
535
+
try
536
+
let json = parse_json json_str in
537
+
let value = parse_json value_str in
538
+
let p = Jsont_pointer.of_string pointer_str in
539
+
let result = Jsont_pointer.set p json ~value in
540
+
Printf.printf "%s\n" (json_to_string result)
541
+
with Jsont.Error e ->
542
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
543
+
544
+
(* Test: jsont codec *)
545
+
let test_jsont_codec pointer_str =
546
+
try
547
+
let json = Jsont.Json.string pointer_str in
548
+
let decoded = match Jsont.Json.decode' Jsont_pointer.jsont json with
549
+
| Ok p -> p
550
+
| Error e -> raise (Jsont.Error e)
551
+
in
552
+
let encoded = match Jsont.Json.encode' Jsont_pointer.jsont decoded with
553
+
| Ok j -> j
554
+
| Error e -> raise (Jsont.Error e)
555
+
in
556
+
let encoded_str = json_to_string encoded in
557
+
Printf.printf "%s\n" encoded_str
558
+
with Jsont.Error e ->
559
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
560
+
561
+
(* Test: jsont_kind codec *)
562
+
let test_jsont_kind pointer_str =
563
+
try
564
+
let json = Jsont.Json.string pointer_str in
565
+
let decoded = match Jsont.Json.decode' Jsont_pointer.jsont_kind json with
566
+
| Ok p -> p
567
+
| Error e -> raise (Jsont.Error e)
568
+
in
569
+
match decoded with
570
+
| `Nav p -> Printf.printf "Nav(%s)\n" (Jsont_pointer.to_string p)
571
+
| `Append p -> Printf.printf "Append(%s)\n" (Jsont_pointer.to_string p)
572
+
with Jsont.Error e ->
573
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
574
+
575
+
(* Test: jsont_nav codec *)
576
+
let test_jsont_nav pointer_str =
577
+
try
578
+
let json = Jsont.Json.string pointer_str in
579
+
let decoded = match Jsont.Json.decode' Jsont_pointer.jsont_nav json with
580
+
| Ok p -> p
581
+
| Error e -> raise (Jsont.Error e)
582
+
in
583
+
Printf.printf "OK: %s\n" (Jsont_pointer.to_string decoded)
584
+
with Jsont.Error e ->
585
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
586
+
587
+
(* Test: jsont_uri_fragment codec *)
588
+
let test_jsont_uri_fragment pointer_str =
589
+
try
590
+
(* First parse it normally, then encode as URI fragment *)
591
+
let p = Jsont_pointer.of_string pointer_str in
592
+
let encoded = match Jsont.Json.encode' Jsont_pointer.jsont_uri_fragment p with
593
+
| Ok j -> j
594
+
| Error e -> raise (Jsont.Error e)
595
+
in
596
+
let encoded_str = json_to_string encoded in
597
+
Printf.printf "%s\n" encoded_str
598
+
with Jsont.Error e ->
599
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
600
+
601
+
(* Test: query combinator - path *)
602
+
let test_query_path json_str pointer_str =
603
+
try
604
+
let json = parse_json json_str in
605
+
let p = Jsont_pointer.of_string_nav pointer_str in
606
+
let codec = Jsont_pointer.path p Jsont.string in
607
+
let result = match Jsont.Json.decode' codec json with
608
+
| Ok v -> v
609
+
| Error e -> raise (Jsont.Error e)
610
+
in
611
+
Printf.printf "OK: %s\n" result
612
+
with Jsont.Error e ->
613
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
614
+
615
+
(* Test: query combinator - path with absent *)
616
+
let test_query_path_absent json_str pointer_str default =
617
+
try
618
+
let json = parse_json json_str in
619
+
let p = Jsont_pointer.of_string_nav pointer_str in
620
+
let codec = Jsont_pointer.path ~absent:default p Jsont.string in
621
+
let result = match Jsont.Json.decode' codec json with
622
+
| Ok v -> v
623
+
| Error e -> raise (Jsont.Error e)
624
+
in
625
+
Printf.printf "OK: %s\n" result
626
+
with Jsont.Error e ->
627
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
628
+
629
+
(* Test: query combinator - set_path *)
630
+
let test_set_path json_str pointer_str value_str =
631
+
try
632
+
let json = parse_json json_str in
633
+
let p = Jsont_pointer.of_string pointer_str in
634
+
let codec = Jsont_pointer.set_path Jsont.string p value_str in
635
+
let result = match Jsont.Json.recode' codec json with
636
+
| Ok v -> v
637
+
| Error e -> raise (Jsont.Error e)
638
+
in
639
+
Printf.printf "%s\n" (json_to_string result)
640
+
with Jsont.Error e ->
641
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
642
+
643
+
(* Test: query combinator - update_path *)
644
+
let test_update_path json_str pointer_str =
645
+
try
646
+
let json = parse_json json_str in
647
+
let p = Jsont_pointer.of_string_nav pointer_str in
648
+
let codec = Jsont_pointer.update_path p Jsont.string in
649
+
let result = match Jsont.Json.recode' codec json with
650
+
| Ok v -> v
651
+
| Error e -> raise (Jsont.Error e)
652
+
in
653
+
Printf.printf "%s\n" (json_to_string result)
654
+
with Jsont.Error e ->
655
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
656
+
657
+
(* Test: query combinator - delete_path *)
658
+
let test_delete_path json_str pointer_str =
659
+
try
660
+
let json = parse_json json_str in
661
+
let p = Jsont_pointer.of_string_nav pointer_str in
662
+
let codec = Jsont_pointer.delete_path p in
663
+
let result = match Jsont.Json.recode' codec json with
664
+
| Ok v -> v
665
+
| Error e -> raise (Jsont.Error e)
666
+
in
667
+
Printf.printf "%s\n" (json_to_string result)
668
+
with Jsont.Error e ->
669
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
670
+
671
+
(* Test: query combinator - delete_path with allow_absent *)
672
+
let test_delete_path_absent json_str pointer_str =
673
+
try
674
+
let json = parse_json json_str in
675
+
let p = Jsont_pointer.of_string_nav pointer_str in
676
+
let codec = Jsont_pointer.delete_path ~allow_absent:true p in
677
+
let result = match Jsont.Json.recode' codec json with
678
+
| Ok v -> v
679
+
| Error e -> raise (Jsont.Error e)
680
+
in
681
+
Printf.printf "%s\n" (json_to_string result)
682
+
with Jsont.Error e ->
683
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
684
+
685
+
(* Test: JMAP of_string_result *)
686
+
let test_jmap_of_string_result pointer_str =
687
+
match Jsont_pointer.Jmap.of_string_result pointer_str with
688
+
| Ok p -> Printf.printf "Ok(%s)\n" (Jsont_pointer.Jmap.to_string p)
689
+
| Error e -> Printf.printf "Error(%s)\n" e
690
+
691
+
(* Test: JMAP pp *)
692
+
let test_jmap_pp pointer_str =
693
+
try
694
+
let p = Jsont_pointer.Jmap.of_string pointer_str in
695
+
Format.printf "%a\n" Jsont_pointer.Jmap.pp p
696
+
with Jsont.Error e ->
697
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
698
+
699
+
(* Test: JMAP eval_result *)
700
+
let test_jmap_eval_result json_str pointer_str =
701
+
try
702
+
let json = parse_json json_str in
703
+
let p = Jsont_pointer.Jmap.of_string pointer_str in
704
+
match Jsont_pointer.Jmap.eval_result p json with
705
+
| Ok result -> Printf.printf "Ok(%s)\n" (json_to_string result)
706
+
| Error e -> Printf.printf "Error(%s)\n" (Jsont.Error.to_string e)
707
+
with Jsont.Error e ->
708
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
709
+
710
+
(* Test: JMAP find *)
711
+
let test_jmap_find json_str pointer_str =
712
+
try
713
+
let json = parse_json json_str in
714
+
let p = Jsont_pointer.Jmap.of_string pointer_str in
715
+
match Jsont_pointer.Jmap.find p json with
716
+
| Some result -> Printf.printf "Some(%s)\n" (json_to_string result)
717
+
| None -> Printf.printf "None\n"
718
+
with Jsont.Error e ->
719
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
720
+
721
+
(* Test: JMAP jsont codec *)
722
+
let test_jmap_jsont pointer_str =
723
+
try
724
+
let json = Jsont.Json.string pointer_str in
725
+
let decoded = match Jsont.Json.decode' Jsont_pointer.Jmap.jsont json with
726
+
| Ok p -> p
727
+
| Error e -> raise (Jsont.Error e)
728
+
in
729
+
let encoded = match Jsont.Json.encode' Jsont_pointer.Jmap.jsont decoded with
730
+
| Ok j -> j
731
+
| Error e -> raise (Jsont.Error e)
732
+
in
733
+
let encoded_str = json_to_string encoded in
734
+
Printf.printf "%s\n" encoded_str
735
+
with Jsont.Error e ->
736
+
Printf.printf "ERROR: %s\n" (Jsont.Error.to_string e)
737
+
289
738
let () =
290
739
match Array.to_list Sys.argv with
291
740
| _ :: "parse" :: pointer :: _ ->
···
328
777
test_jmap_path_single json pointer
329
778
| _ :: "jmap-path-absent" :: json :: pointer :: default :: _ ->
330
779
test_jmap_path_absent json pointer default
780
+
(* Index functions *)
781
+
| _ :: "mem" :: name :: _ ->
782
+
test_mem name
783
+
| _ :: "nth" :: n :: _ ->
784
+
test_nth (int_of_string n)
785
+
| _ :: "equal-index" :: idx1 :: idx2 :: _ ->
786
+
test_equal_index idx1 idx2
787
+
| _ :: "compare-index" :: idx1 :: idx2 :: _ ->
788
+
test_compare_index idx1 idx2
789
+
(* Pointer constructors *)
790
+
| _ :: "root" :: _ ->
791
+
test_root ()
792
+
| _ :: "is-root" :: pointer :: _ ->
793
+
test_is_root pointer
794
+
| _ :: "make" :: indices :: _ ->
795
+
test_make indices
796
+
| _ :: "append-index" :: base :: index :: _ ->
797
+
test_append_index base index
798
+
| _ :: "at-end" :: pointer :: _ ->
799
+
test_at_end pointer
800
+
| _ :: "concat" :: p1 :: p2 :: _ ->
801
+
test_concat p1 p2
802
+
| _ :: "parent" :: pointer :: _ ->
803
+
test_parent pointer
804
+
| _ :: "last" :: pointer :: _ ->
805
+
test_last pointer
806
+
| _ :: "indices" :: pointer :: _ ->
807
+
test_indices pointer
808
+
(* Coercion *)
809
+
| _ :: "to-nav" :: pointer :: _ ->
810
+
test_to_nav pointer
811
+
| _ :: "to-nav-exn" :: pointer :: _ ->
812
+
test_to_nav_exn pointer
813
+
(* Parsing variants *)
814
+
| _ :: "of-string-kind" :: pointer :: _ ->
815
+
test_of_string_kind pointer
816
+
| _ :: "of-string-result" :: pointer :: _ ->
817
+
test_of_string_result pointer
818
+
| _ :: "of-uri-fragment-nav" :: frag :: _ ->
819
+
test_of_uri_fragment_nav frag
820
+
| _ :: "of-uri-fragment-result" :: frag :: _ ->
821
+
test_of_uri_fragment_result frag
822
+
(* Pretty printing *)
823
+
| _ :: "pp" :: pointer :: _ ->
824
+
test_pp pointer
825
+
| _ :: "pp-verbose" :: pointer :: _ ->
826
+
test_pp_verbose pointer
827
+
(* Comparison *)
828
+
| _ :: "equal" :: p1 :: p2 :: _ ->
829
+
test_equal p1 p2
830
+
| _ :: "compare" :: p1 :: p2 :: _ ->
831
+
test_compare p1 p2
832
+
(* Path conversion *)
833
+
| _ :: "of-path" :: _ ->
834
+
test_of_path ()
835
+
| _ :: "to-path" :: pointer :: _ ->
836
+
test_to_path pointer
837
+
(* Evaluation *)
838
+
| _ :: "get-result" :: json :: pointer :: _ ->
839
+
test_get_result json pointer
840
+
| _ :: "set" :: json :: pointer :: value :: _ ->
841
+
test_set json pointer value
842
+
(* Jsont codecs *)
843
+
| _ :: "jsont-codec" :: pointer :: _ ->
844
+
test_jsont_codec pointer
845
+
| _ :: "jsont-kind" :: pointer :: _ ->
846
+
test_jsont_kind pointer
847
+
| _ :: "jsont-nav" :: pointer :: _ ->
848
+
test_jsont_nav pointer
849
+
| _ :: "jsont-uri-fragment" :: pointer :: _ ->
850
+
test_jsont_uri_fragment pointer
851
+
(* Query combinators *)
852
+
| _ :: "query-path" :: json :: pointer :: _ ->
853
+
test_query_path json pointer
854
+
| _ :: "query-path-absent" :: json :: pointer :: default :: _ ->
855
+
test_query_path_absent json pointer default
856
+
| _ :: "set-path" :: json :: pointer :: value :: _ ->
857
+
test_set_path json pointer value
858
+
| _ :: "update-path" :: json :: pointer :: _ ->
859
+
test_update_path json pointer
860
+
| _ :: "delete-path" :: json :: pointer :: _ ->
861
+
test_delete_path json pointer
862
+
| _ :: "delete-path-absent" :: json :: pointer :: _ ->
863
+
test_delete_path_absent json pointer
864
+
(* JMAP extras *)
865
+
| _ :: "jmap-of-string-result" :: pointer :: _ ->
866
+
test_jmap_of_string_result pointer
867
+
| _ :: "jmap-pp" :: pointer :: _ ->
868
+
test_jmap_pp pointer
869
+
| _ :: "jmap-eval-result" :: json :: pointer :: _ ->
870
+
test_jmap_eval_result json pointer
871
+
| _ :: "jmap-find" :: json :: pointer :: _ ->
872
+
test_jmap_find json pointer
873
+
| _ :: "jmap-jsont" :: pointer :: _ ->
874
+
test_jmap_jsont pointer
331
875
| _ ->
332
876
Printf.printf "Usage:\n";
333
877
Printf.printf " test_pointer parse <pointer>\n";
···
346
890
Printf.printf " test_pointer jmap-parse <pointer>\n";
347
891
Printf.printf " test_pointer jmap-eval <json> <pointer>\n";
348
892
Printf.printf " test_pointer jmap-eval-file <json-file> <pointer>\n";
893
+
Printf.printf " -- Index functions --\n";
894
+
Printf.printf " test_pointer mem <name>\n";
895
+
Printf.printf " test_pointer nth <n>\n";
896
+
Printf.printf " test_pointer equal-index <idx1> <idx2>\n";
897
+
Printf.printf " test_pointer compare-index <idx1> <idx2>\n";
898
+
Printf.printf " -- Pointer constructors --\n";
899
+
Printf.printf " test_pointer root\n";
900
+
Printf.printf " test_pointer is-root <pointer>\n";
901
+
Printf.printf " test_pointer make <indices>\n";
902
+
Printf.printf " test_pointer append-index <base> <index>\n";
903
+
Printf.printf " test_pointer at-end <pointer>\n";
904
+
Printf.printf " test_pointer concat <p1> <p2>\n";
905
+
Printf.printf " test_pointer parent <pointer>\n";
906
+
Printf.printf " test_pointer last <pointer>\n";
907
+
Printf.printf " test_pointer indices <pointer>\n";
908
+
Printf.printf " -- Coercion --\n";
909
+
Printf.printf " test_pointer to-nav <pointer>\n";
910
+
Printf.printf " test_pointer to-nav-exn <pointer>\n";
911
+
Printf.printf " -- Parsing variants --\n";
912
+
Printf.printf " test_pointer of-string-kind <pointer>\n";
913
+
Printf.printf " test_pointer of-string-result <pointer>\n";
914
+
Printf.printf " test_pointer of-uri-fragment-nav <frag>\n";
915
+
Printf.printf " test_pointer of-uri-fragment-result <frag>\n";
916
+
Printf.printf " -- Pretty printing --\n";
917
+
Printf.printf " test_pointer pp <pointer>\n";
918
+
Printf.printf " test_pointer pp-verbose <pointer>\n";
919
+
Printf.printf " -- Comparison --\n";
920
+
Printf.printf " test_pointer equal <p1> <p2>\n";
921
+
Printf.printf " test_pointer compare <p1> <p2>\n";
922
+
Printf.printf " -- Path conversion --\n";
923
+
Printf.printf " test_pointer of-path\n";
924
+
Printf.printf " test_pointer to-path <pointer>\n";
925
+
Printf.printf " -- Evaluation --\n";
926
+
Printf.printf " test_pointer get-result <json> <pointer>\n";
927
+
Printf.printf " test_pointer set <json> <pointer> <value>\n";
928
+
Printf.printf " -- Jsont codecs --\n";
929
+
Printf.printf " test_pointer jsont-codec <pointer>\n";
930
+
Printf.printf " test_pointer jsont-kind <pointer>\n";
931
+
Printf.printf " test_pointer jsont-nav <pointer>\n";
932
+
Printf.printf " test_pointer jsont-uri-fragment <pointer>\n";
933
+
Printf.printf " -- Query combinators --\n";
934
+
Printf.printf " test_pointer query-path <json> <pointer>\n";
935
+
Printf.printf " test_pointer query-path-absent <json> <pointer> <default>\n";
936
+
Printf.printf " test_pointer set-path <json> <pointer> <value>\n";
937
+
Printf.printf " test_pointer update-path <json> <pointer>\n";
938
+
Printf.printf " test_pointer delete-path <json> <pointer>\n";
939
+
Printf.printf " test_pointer delete-path-absent <json> <pointer>\n";
940
+
Printf.printf " -- JMAP extras --\n";
941
+
Printf.printf " test_pointer jmap-of-string-result <pointer>\n";
942
+
Printf.printf " test_pointer jmap-pp <pointer>\n";
943
+
Printf.printf " test_pointer jmap-eval-result <json> <pointer>\n";
944
+
Printf.printf " test_pointer jmap-find <json> <pointer>\n";
945
+
Printf.printf " test_pointer jmap-jsont <pointer>\n";
349
946
exit 1