···11+v0.2.0 2025-07-25 Zagreb
22+------------------------
33+44+- Fix `Jsont_bytesrw.{encode,encode'}`. Do not write the `eod` slice if
55+ `eod:false` is specified. Thanks to Benjamin Nguyen-Van-Yen for
66+ the report and the fix (#8).
77+- Fix `Jsont.zero` failing encodes rather than encoding `null` as
88+ advertised. Thanks to Adrián Montesinos González for the report (#6).
99+- Add `Jsont.Error.expected` to help format error messages.
1010+- Add `Jsont.with_doc` to update kind and doc strings of existing JSON
1111+ types.
1212+- Add `Jsont.Object.Case.{tag,map_tag}` to access a case and case map tags.
1313+- Fix `META` file. Really export all requires and
1414+ remove uneeded `bytesrw` dependency from `jsont` library.
1515+1616+v0.1.1 2024-12-06 La Forclaz (VS)
1717+---------------------------------
1818+1919+- `Jsont.Object.Mems.map` make encoding and decoding optional. Like
2020+ in every other map.
2121+- `Jsont.Array.map` make encoding and decoding optional. Like
2222+ in every other map.
2323+- `Jsont_bytesrw.encode` change the default buffer size
2424+ to match the one hinted by the writer rather than
2525+ `Bytesrw.Bytes.Slice.io_buffer_size`.
2626+- `jsont.{bytesrw,brr}` export all requires.
2727+- `jsont` tool remove spurious dependency on `b0.std` (#2).
2828+2929+v0.1.0 2024-11-29 Zagreb
3030+------------------------
3131+3232+First release.
3333+3434+Supported by a grant from the OCaml Software Foundation.
+35
vendor/opam/jsont/DEVEL.md
···11+This project uses (perhaps the development version of) [`b0`] for
22+development. Consult [b0 occasionally] for quick hints on how to
33+perform common development tasks.
44+55+[`b0`]: https://erratique.ch/software/b0
66+[b0 occasionally]: https://erratique.ch/software/b0/doc/occasionally.html
77+88+# Testing
99+1010+ b0 test
1111+1212+# Testing the codec with Nicolas Seriot's test suite
1313+1414+ b0 -- download-seriot-suite
1515+ b0 test
1616+1717+# Benchmarking
1818+1919+## Decode only
2020+2121+ hyperfine 'json_xs -t none < tmp/parcels.json'
2222+ hyperfine 'jsontrip -dec tmp/parcels.json'
2323+ hyperfine "$(b0 --path -- jsont) fmt -d tmp/parcels.json"
2424+ hyperfine "$(b0 --path -- geojson) -d tmp/parcels.json"
2525+2626+## Decode and minify
2727+2828+ hyperfine 'json_xs -t json < tmp/parcels.json'
2929+ hyperfine 'jq -c . tmp/parcels.json'
3030+ hyperfine 'ydump -std -c tmp/parcels.json'
3131+ hyperfine 'jsontrip tmp/parcels.json'
3232+ hyperfine "$(b0 --path -- jsont) fmt -fminify tmp/parcels.json"
3333+ hyperfine "$(b0 --path -- geojson) tmp/parcels.json"
3434+3535+
+15
vendor/opam/jsont/LICENSE.md
···11+ISC License
22+33+Copyright (c) 2024 The jsont programmers
44+55+Permission to use, copy, modify, and/or distribute this software for any
66+purpose with or without fee is hereby granted, provided that the above
77+copyright notice and this permission notice appear in all copies.
88+99+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1010+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1111+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1212+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1313+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1414+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1515+PERFORMANCE OF THIS SOFTWARE.
+70
vendor/opam/jsont/README.md
···11+Jsont – Declarative JSON data manipulation for OCaml
22+====================================================
33+44+Jsont is an OCaml library for declarative JSON data manipulation. It
55+provides:
66+77+- Combinators for describing JSON data using the OCaml values of your
88+ choice. The descriptions can be used by generic functions to
99+ decode, encode, query and update JSON data without having to
1010+ construct a generic JSON representation.
1111+- A JSON codec with optional text location tracking and layout
1212+ preservation. The codec is compatible with effect-based concurrency.
1313+1414+The descriptions are independent from the codec and can be used by
1515+third-party processors or codecs.
1616+1717+Jsont is distributed under the ISC license. It has no dependencies.
1818+The codec is optional and depends on the [`bytesrw`] library. The JavaScript
1919+support is optional and depends on the [`brr`] library.
2020+2121+Homepage: <https://erratique.ch/software/jsont/>
2222+2323+[`bytesrw`]: https://erratique.ch/software/bytesrw
2424+[`brr`]: https://erratique.ch/software/brr
2525+2626+## Installation
2727+2828+Jsont can be installed with `opam`:
2929+3030+ opam install jsont
3131+ opam install jsont bytesrw # For the optional codec support
3232+ opam install jsont brr # For the optional JavaScript support
3333+ opam install jsont bytesrw cmdliner # For the jsont tool
3434+3535+## Documentation
3636+3737+The documentation can be consulted [online] or via `odig doc jsont`.
3838+3939+Questions are welcome but better asked on the [OCaml forum] than on the
4040+issue tracker.
4141+4242+[online]: https://erratique.ch/software/jsont/doc
4343+[OCaml forum]: https://discuss.ocaml.org/
4444+4545+## Examples
4646+4747+A few examples can be found in the [documentation][online] and in the
4848+[test](test/) directory. The [`test/topojson.ml`],
4949+[`test/geojson.ml`], [`test/json_rpc.ml`], show use of the library on
5050+concrete JSON data formats.
5151+5252+[`test/topojson.ml`]: test/topojson.ml
5353+[`test/geojson.ml`]: test/geojson.ml
5454+[`test/json_rpc.ml`]: test/json_rpc.ml
5555+5656+## Paper & technique
5757+5858+If you want to understand the *finally tagged* technique used by the
5959+library, the [`paper/soup.ml`] source implements the abridged version
6060+of the underlying data type used in [the paper].
6161+6262+[the paper]: paper/
6363+[`paper/soup.ml`]: paper/soup.ml
6464+6565+## Acknowledgments
6666+6767+A grant from the [OCaml Software Foundation] helped to bring the first
6868+public release of `jsont`.
6969+7070+[OCaml Software Foundation]: http://ocaml-sf.org/
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* This syntax/idea does not work well with JSON it worked well with
77+ s-expressions because of their uniform nature e.g. to insert
88+ bindings.
99+1010+ That would still work on arrays though. Maybe we could add
1111+ something at some point. *)
1212+1313+module Path : sig
1414+1515+1616+ (** {1:carets Carets} *)
1717+1818+ (** Carets.
1919+2020+ A path and a spatial localisation. *)
2121+ module Caret : sig
2222+2323+ (** {1:caret Carets} *)
2424+2525+ type path := t
2626+2727+ type pos =
2828+ | Before (** The void before the data indexed by a path. *)
2929+ | Over (** The data indexed by a path. *)
3030+ | After (** The void after the data indexed by a path. *)
3131+ (** The type for caret positions. *)
3232+3333+ type t = pos * path
3434+ (** The type for carets. A path and a caret position. *)
3535+3636+ val of_string : string -> (t, string) result
3737+ (** [of_string s] parses a caret according to
3838+ the {{!path_caret_syntax}caret syntax} .*)
3939+4040+ val pp : t fmt
4141+ (** [pp] formats carets. *)
4242+ end
4343+4444+ val over : t -> Caret.t
4545+ (** [over p] is the data at the path [p]. *)
4646+4747+ val before : t -> Caret.t
4848+ (** [before p] is the void before the path [p]. *)
4949+5050+ val after : t -> Caret.t
5151+ (** [after p] is the void after the path [p]. *)
5252+5353+ (** {1:path_caret_syntax Path & caret syntax}
5454+5555+ Path and carets provide a way for end users to address JSON and
5656+ edit locations.
5757+5858+ A {e path} is a sequence of member and list indexing
5959+ operations. Applying the path to a JSON value leads to either a
6060+ JSON value or nothing if one of the indices does not exist, or
6161+ an error if ones tries to index a non-indexable value.
6262+6363+ A {e caret} is a path and a spatial specification for the JSON
6464+ construct found by the path. The caret indicates either the void
6565+ {e before} that JSON construct, the JSON value itself ({e over}) or
6666+ the void {e after} it.
6767+6868+ Here are a few examples of paths and carets, syntactically the
6969+ charater ['v'] is used to denote the caret's insertion point before or
7070+ after a path. There's no distinction between a path and an over caret.
7171+7272+ {@json[
7373+ {
7474+ "ocaml": {
7575+ "libs": ["jsont", "brr", "cmdliner"]
7676+ }
7777+ }
7878+ ]}
7979+8080+ {@shell[
8181+ ocaml.libs # value of member "libs" of member "ocaml"
8282+ ocaml.v[libs] # void before the "libs" member
8383+ ocaml.[libs]v # void after "libs" member
8484+8585+ ocaml.libs.[0] # first element of member "libs" of member "ocaml"
8686+ ocaml.libs.v[0] # void before first element
8787+ ocaml.libs.[0]v # void after first element
8888+8989+ ocaml.libs.[-1] # last element of member "libs" of member "ocaml"
9090+ ocaml.libs.v[-1] # before last element (if any)
9191+ ocaml.libs.[-1]v # after last element (if any)
9292+ ]}
9393+9494+ More formally a {e path} is a [.] seperated list of indices.
9595+9696+ An {e index} is written [[i]]. [i] can a zero-based list index
9797+ with negative indices counting from the end of the list ([-1] is
9898+ the last element). Or [i] can be an object member name [n]. If
9999+ there is no ambiguity, the surrounding brackets can be dropped.
100100+101101+ A {e caret} is a path whose last index brackets can be prefixed or
102102+ suffixed by an insertion point, represented by the character
103103+ ['v']. This respectively denote the void before or after the
104104+ JSON construct found by the path.
105105+106106+ {b Notes.}
107107+ {ul
108108+ {- The syntax has no form of quoting at the moment this
109109+ means key names can't contain, [\[], [\]], or start with a number.}
110110+ {- It would be nice to be able to drop the dots in order
111111+ to be compatible with {{:https://www.rfc-editor.org/rfc/rfc9535}
112112+ JSONPath} syntax.}} *)
113113+end = struct
114114+115115+116116+ (* Carets *)
117117+118118+ module Caret = struct
119119+ type path = t
120120+ type pos = Before | Over | After
121121+ type t = pos * path
122122+ let pp ppf = function
123123+ | Over, p -> pp ppf p
124124+ | Before, (c :: p)->
125125+ pp ppf p;
126126+ (if p <> [] then Fmt.char ppf '.');
127127+ Fmt.char ppf 'v'; pp_bracketed_index ppf c
128128+ | After, (c :: p) ->
129129+ pp ppf p;
130130+ (if p <> [] then Fmt.char ppf '.');
131131+ pp_bracketed_index ppf c; Fmt.char ppf 'v'
132132+ | _ -> ()
133133+134134+ (* Parsing *)
135135+136136+ let of_string s =
137137+ let rec loop p s i max =
138138+ if i > max then Over, p else
139139+ let next = i + 1 in
140140+ match s.[i] with
141141+ | 'v' when next <= max && s.[next] = '[' ->
142142+ let next, p = parse_index p s next max in
143143+ parse_eoi s next max; Before, p
144144+ | c ->
145145+ let next, p = parse_index p s i max in
146146+ if next > max then Over, p else
147147+ if s.[next] = 'v'
148148+ then (parse_eoi s (next + 1) max; After, p) else
149149+ if s.[next] <> '.' then err_unexp_char next s else
150150+ if next + 1 <= max then loop p s (next + 1) max else
151151+ err_unexp_eoi next
152152+ in
153153+ try
154154+ if s = "" then Ok (Over, []) else
155155+ let start = if s.[0] = '.' then 1 else 0 in
156156+ Ok (loop [] s start (String.length s - 1))
157157+ with Failure e -> Error e
158158+ end
159159+160160+ let over p = Caret.Over, p
161161+ let after p = Caret.After, p
162162+ let before p = Caret.Before, p
163163+end
+59
vendor/opam/jsont/attic/json_stat.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* https://json-stat.org/ *)
77+88+open Jsonit
99+1010+module Int_map = Map.Make (Int)
1111+module String_map = Map.Make (String)
1212+1313+type 'a vec = Array of 'a list | Sparse of 'a Int_map.t
1414+1515+type status =
1616+| All of string
1717+| Vec of string vec
1818+1919+type index = (* ?? *)
2020+| Array of string list
2121+| Map of int String_map.t
2222+2323+type category =
2424+ { index : index;
2525+ label : string String_map.t }
2626+2727+type date =
2828+ (* https://262.ecma-international.org/6.0/#sec-date-time-string-format *)
2929+ string
3030+3131+module Dimension_id = struct
3232+ type t =
3333+ { category : Json.obj;
3434+ label : string option;
3535+ extension : Json.obj option; }
3636+end
3737+3838+3939+type dataset =
4040+ { id : string list;
4141+ size : int list;
4242+ value : float vec;
4343+ dimension : Dimension_id.t String_map.t;
4444+ status : status vec option;
4545+ label : string option;
4646+ source : string option;
4747+ updated : date option;
4848+ extension : Json.obj option; }
4949+5050+type collection = unit
5151+5252+type class' =
5353+| Dataset of dataset
5454+| Dimension of Dimension_id.t
5555+| Collection of collection
5656+5757+type t =
5858+ { version : string;
5959+ class' : class'; }
+536
vendor/opam/jsont/doc/cookbook.mld
···11+{0 [Jsont] cookbook}
22+33+A few conventions and recipes to describe JSON data with
44+{!Jsont}.
55+66+{1:conventions Conventions}
77+88+{2:naming Naming {!Jsont.t} values}
99+1010+Given an OCaml type [t] its JSON type value should be called
1111+[t_jsont]. If your type follows the [M.t] module convention use
1212+[M.jsont].
1313+1414+{1:tips General tips}
1515+1616+Note that constructing {!Jsont.t} values has a cost. In particular
1717+when object descriptions are {!Jsont.Object.finish}ed a few checks are
1818+performed on the definition. Hence it's better to construct them as
1919+toplevel values or at least make sure you are not repeatedly
2020+constructing them dynamically in a tight loop.
2121+2222+{2:general_erroring Erroring}
2323+2424+Jsont types are full of your functions that you specify to implement
2525+the decoding and encoding process (e.g. base map decoding and encoding
2626+functions, object map constructors, object map member projectors,
2727+etc.). In general in any of these functions it is always safe to error
2828+by raising the {!Jsont.exception-Error} exception if you need to.
2929+3030+Use the functions in the {!Jsont.module-Error} to format error
3131+messages. They usually require to specify a {!Jsont.Meta.t} value to
3232+precisely locate the error. If you have none to provide simply use
3333+{!Jsont.Meta.none}.
3434+3535+{1:dealing_with_null Dealing with [null] values}
3636+3737+Nullable JSON values are naturally mapped to ocaml [option] types. The
3838+{!Jsont.val-option} combinator does exactly that.
3939+4040+It is also possible to map JSON [null]s to a default value with
4141+{!Jsont.null}. This can then be combined with {!Jsont.val-any} to compose
4242+with other JSON types.
4343+4444+For example the following maps JSON [null]s to [""] and JSON strings
4545+to [string] on decoding. On encoding we unconditionally map back [""]
4646+to [null]:
4747+4848+{[
4949+let string_null_is_empty =
5050+ let null = Jsont.null "" in
5151+ let enc = function "" -> null | _ -> Jsont.string in
5252+ Jsont.any ~dec_null:null ~dec_string:Jsont.string ~enc ()
5353+]}
5454+5555+See also {!non_finite_numbers} and the tangentially related topic of
5656+{!optional_members}.
5757+5858+{1:dealing_with_numbers Dealing with numbers}
5959+6060+JSON is utterly broken to interchange numbers reliably as the standard
6161+provides no constraints on their representation. Generally interopable
6262+implementations, in particular the most widely deployed and formally
6363+specified ECMAScript implementation, use IEEE 754 [binary64] values to
6464+represent finite JSON numbers and [null] values to represent
6565+non-finite one. This has the following consequences.
6666+6767+{2:integer_numbers Integer numbers}
6868+6969+For representing integers by JSON numbers one is limited to the range
7070+\[-2{^53};2{^53}\] which are the only integers represented
7171+precisely in IEEE 754 [binary64]. If you want to serialize numbers
7272+beyond this range you need to represent them by a JSON string. These
7373+scheme can be seen in the wild:
7474+{ul
7575+{- Integers are unconditionally represented by strings. In this case
7676+ {!Jsont.int_as_string} or {!Jsont.int64_as_string} can be used.}
7777+{- Integers are represented by numbers or strings depending on their
7878+ magnitude. In this case {!Jsont.int} or {!Jsont.int64}
7979+ can be used.}
8080+{- The integer range of interest can be fully represented in a JSON number.
8181+ In this case {!Jsont.int8}, {!Jsont.uint8}, {!Jsont.int16}, etc. can be
8282+ used.}}
8383+8484+{2:non_finite_numbers Non-finite numbers}
8585+8686+JSON numbers cannot represent IEEE 754 [binary64] numbers: infinities
8787+and NaNs cannot be represented. The formally defined
8888+{{:https://tc39.es/ecma262/multipage/structured-data.html#sec-serializejsonproperty}ECMAScript's
8989+[JSON.stringify]} function replaces these values by [null].
9090+9191+For this reason in [Jsont] the domain of {!Jsont.Base.number} maps is
9292+JSON numbers {e or JSON null}. In the decoding direction a null is
9393+mapped to {!Float.nan} and in the encoding direction any float not
9494+satisfying {!Float.is_finite} is mapped to a JSON null.
9595+9696+If you can agree with a third party on a better encoding, the
9797+{!Jsont.any_float} or {!Jsont.float_as_hex_string} provide
9898+lossless representations of IEEE 754 [binary64] values in JSON.
9999+100100+{1:base_types Transforming base types}
101101+102102+The {!Jsont.map} combinator is a general map over {!Jsont.t} types.
103103+It should rather be used to alter the representation of existing
104104+{!Jsont.t} values. For transforming base types it is better to use the
105105+base maps of {!Jsont.Base} as more context is made available to the
106106+functions, notably when erroring.
107107+108108+{2:transform_strings Transforming strings}
109109+110110+A few simple JSON string transformers like {!Jsont.enum} or
111111+{!Jsont.binary_string} are provided.
112112+113113+If you need to devise your own maps from your own [M.{of,to}_string]
114114+functions that return [result] or raise [Faiulre _] you can adapt them
115115+with {{!Jsont.Base.decenc}these functions}. For example:
116116+{[
117117+let m_jsont =
118118+ let dec = Jsont.Base.dec_result M.result_of_string in
119119+ let enc = Jsont.Base.enc M.to_string in
120120+ Jsont.Base.string (Jsont.Base.map ~kind:"M.t" ~dec ~enc ())
121121+122122+let m_jsont' =
123123+ let dec = Jsont.Base.dec_failure M.of_string_or_failure in
124124+ let enc = Jsont.Base.enc M.to_string in
125125+ Jsont.Base.string (Jsont.Base.map ~kind:"M.t" ~dec ~enc ())
126126+]}
127127+128128+If you are dealing with result decoders you can also simply
129129+use {!Jsont.of_of_string}:
130130+131131+{[
132132+let m_jsont'' =
133133+ Jsont.of_of_string ~kind:"M.t" M.result_of_string ~enc:M.to_string
134134+]}
135135+136136+which is a shortcut for the [m_jsont] written above.
137137+138138+{1:dealing_with_arrays Dealing with arrays}
139139+140140+JSON arrays can be directly mapped to OCaml {{!Jsont.list}lists},
141141+{{!Jsont.array}arrays}, {{!Jsont.bigarray}bigarray} or bespoke
142142+low-dimensional {{!Jsont.t2}tuples}. If your JSON is an array of
143143+objects keyed by some identifier you may find
144144+{!Jsont.array_as_string_map} handy.
145145+146146+If none of that fits you can always devise your own {!Jsont.Array.val-map}.
147147+148148+{1:dealing_with_objects Dealing with objects}
149149+150150+{2:objects_as_records Objects as records}
151151+152152+Suppose our JSON object is:
153153+154154+{@json[
155155+{ "name": "Jane Doe"
156156+ "age": 56 }
157157+]}
158158+159159+We represent it with an OCaml record as follows:
160160+161161+{[
162162+module Person = struct
163163+ type t = { name : string; age : int }
164164+ let make name age = { name; age }
165165+ let name p = p.name
166166+ let age p = p.age
167167+ let jsont =
168168+ Jsont.Object.map ~kind:"Person" make
169169+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
170170+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
171171+ |> Jsont.Object.finish
172172+end
173173+]}
174174+175175+{2:objects_as_maps Objects as key-value maps}
176176+177177+JSON objects can be used as maps from strings to a single type
178178+of value ({{:https://github.com/topojson/topojson-specification/blob/7939fe0834f36af8b935ec1827cb0abdd1e34d36/README.md#215-objects}example}).
179179+Such maps can be easily converted to OCaml as follows:
180180+181181+{[
182182+module String_map = Map.Make (String)
183183+184184+let map : ?kind:string -> 'a Jsont.t -> 'a String_map.t Jsont.t =
185185+fun ?kind t ->
186186+ Jsont.Object.map ?kind Fun.id
187187+ |> Jsont.Object.keep_unknown (Jsont.Object.Mems.string_map t) ~enc:Fun.id
188188+ |> Jsont.Object.finish
189189+]}
190190+191191+Since the pattern is common this is directly exposed as
192192+{!Jsont.Object.as_string_map}.
193193+194194+{2:optional_members Optional members}
195195+196196+By default members specified via {!Jsont.Object.mem} are mandatory and
197197+decoding errors if the member is absent.
198198+199199+For those cases where the member is optional a default [dec_absent] value must
200200+be specified to use on decoding when absent. For encoding an
201201+[enc_omit] function can be specified to determine whether the member
202202+should be omitted on encoding.
203203+204204+In the following example we use an option type to denote the potential
205205+absence of the [age] member:
206206+207207+{[
208208+module Person_opt_age = struct
209209+ type t = { name : string; age : int option }
210210+ let make name age = { name; age }
211211+ let name p = p.name
212212+ let age p = p.age
213213+ let jsont =
214214+ Jsont.Object.map ~kind:"Person" make
215215+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
216216+ |> Jsont.Object.mem "age" Jsont.(some int)
217217+ ~dec_absent:None ~enc_omit:Option.is_none ~enc:age
218218+ |> Jsont.Object.finish
219219+end
220220+]}
221221+222222+When absence is represented by [None] like here the
223223+{!Jsont.Object.opt_mem} function can be used. It's stricly equivalent to
224224+the above but more concise.
225225+226226+{2:unknown_members Unknown object members}
227227+228228+In JSON objects maps, there are three different ways to handle
229229+object members that have not been declared by a {!Jsont.Object.mem}
230230+or {!Jsont.Object.opt_mem}.
231231+232232+{3:skipping Skipping}
233233+234234+By default {!Jsont.Object.val-map} skips unknown object members.
235235+236236+{3:erroring Erroring}
237237+238238+To error on unknown members use {!Jsont.Object.val-error_unknown}:
239239+{[
240240+module Person_strict = struct
241241+ type t = { name : string; age : int; }
242242+ let make name age = { name; age }
243243+ let name p = p.name
244244+ let age p = p.age
245245+ let jsont =
246246+ Jsont.Object.map ~kind:"Person" make
247247+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
248248+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
249249+ |> Jsont.Object.error_unknown
250250+ |> Jsont.Object.finish
251251+end
252252+]}
253253+254254+{3:keeping Keeping}
255255+256256+If a JSON data schema allows foreign members or to partially model an
257257+object, unknown members can be collected into a generic
258258+{!Jsont.Json.t} object and stored in an OCaml field by using
259259+{!Jsont.Object.keep_unknown} and {!Jsont.json_mems}:
260260+261261+{[
262262+module Person_keep = struct
263263+ type t = { name : string; age : int; unknown : Jsont.json ; }
264264+ let make name age unknown = { name; age; unknown }
265265+ let name p = p.name
266266+ let age p = p.age
267267+ let unknown v = v.unknown
268268+ let jsont =
269269+ Jsont.Object.map ~kind:"Person" make
270270+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
271271+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
272272+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
273273+ |> Jsont.Object.finish
274274+end
275275+]}
276276+277277+The value of the [unknown] field can be further queried with other
278278+JSON types and {!Jsont.Json.val-decode}. It is also possible to define
279279+your own data structure to keep unknown members, see
280280+{!Jsont.Object.Mems}. See also {!objects_as_maps}.
281281+282282+{2:cases Object types or classes}
283283+284284+Sometimes JSON objects have a distinguished case member, called
285285+["type"], ["class"] or ["version"] whose value define the rest of the
286286+object.
287287+288288+The {!Jsont.Object.Case} module handles this pattern. Each case is
289289+described by a {!Jsont.Object.type-map} object description and the
290290+{!Jsont.Object.case_mem} allows to chose between them according to the
291291+value of the case member.
292292+293293+In OCaml there are two main ways to represent these case objects.
294294+Either by an enclosing variant type with one case for each object kind:
295295+{[
296296+type t = C1 of C1.t | C2 of C2.t | …
297297+]}
298298+or with a record which holds common fields an a field that holds the cases:
299299+{[
300300+type type' = C1 of C1.t | C2 of C2.t | …
301301+type t = { type' : type'; … (* other common fields *) }
302302+]}
303303+From {!Jsont}'s perspective there is not much difference.
304304+305305+We show both modellings on a hypothetic [Geometry] object which has a
306306+["name"] member and a ["type"] string case member indicating whether
307307+the object is a ["Circle"] or a ["Rect"]. Except for the position of
308308+the [name] field, not much changes in each modelling.
309309+310310+Using an enclosing variant type:
311311+312312+{[
313313+module Geometry_variant = struct
314314+ module Circle = struct
315315+ type t = { name : string; radius : float; }
316316+ let make name radius = { name; radius }
317317+ let name c = c.name
318318+ let radius c = c.radius
319319+ let jsont =
320320+ Jsont.Object.map ~kind:"Circle" make
321321+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
322322+ |> Jsont.Object.mem "radius" Jsont.number ~enc:radius
323323+ |> Jsont.Object.finish
324324+ end
325325+326326+ module Rect = struct
327327+ type t = { name : string; width : float; height : float }
328328+ let make name width height = { name; width; height }
329329+ let name r = r.name
330330+ let width r = r.width
331331+ let height r = r.height
332332+ let jsont =
333333+ Jsont.Object.map ~kind:"Rect" make
334334+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
335335+ |> Jsont.Object.mem "width" Jsont.number ~enc:width
336336+ |> Jsont.Object.mem "height" Jsont.number ~enc:height
337337+ |> Jsont.Object.finish
338338+ end
339339+340340+ type t = Circle of Circle.t | Rect of Rect.t
341341+ let circle c = Circle c
342342+ let rect r = Rect r
343343+ let jsont =
344344+ let circle = Jsont.Object.Case.map "Circle" Circle.jsont ~dec:circle in
345345+ let rect = Jsont.Object.Case.map "Rect" Rect.jsont ~dec:rect in
346346+ let enc_case = function
347347+ | Circle c -> Jsont.Object.Case.value circle c
348348+ | Rect r -> Jsont.Object.Case.value rect r
349349+ in
350350+ let cases = Jsont.Object.Case.[make circle; make rect] in
351351+ Jsont.Object.map ~kind:"Geometry" Fun.id
352352+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
353353+ |> Jsont.Object.finish
354354+end
355355+]}
356356+357357+Using a record with a [type'] field:
358358+359359+{[
360360+module Geometry_record = struct
361361+ module Circle = struct
362362+ type t = { radius : float; }
363363+ let make radius = { radius }
364364+ let radius c = c.radius
365365+ let jsont =
366366+ Jsont.Object.map ~kind:"Circle" make
367367+ |> Jsont.Object.mem "radius" Jsont.number ~enc:radius
368368+ |> Jsont.Object.finish
369369+ end
370370+371371+ module Rect = struct
372372+ type t = { width : float; height : float }
373373+ let make width height = { width; height }
374374+ let width r = r.width
375375+ let height r = r.height
376376+ let jsont =
377377+ Jsont.Object.map ~kind:"Rect" make
378378+ |> Jsont.Object.mem "width" Jsont.number ~enc:width
379379+ |> Jsont.Object.mem "height" Jsont.number ~enc:height
380380+ |> Jsont.Object.finish
381381+ end
382382+383383+ type type' = Circle of Circle.t | Rect of Rect.t
384384+ let circle c = Circle c
385385+ let rect r = Rect r
386386+387387+ type t = { name : string; type' : type' }
388388+ let make name type' = { name; type' }
389389+ let name g = g.name
390390+ let type' g = g.type'
391391+392392+ let jsont =
393393+ let circle = Jsont.Object.Case.map "Circle" Circle.jsont ~dec:circle in
394394+ let rect = Jsont.Object.Case.map "Rect" Rect.jsont ~dec:rect in
395395+ let enc_case = function
396396+ | Circle c -> Jsont.Object.Case.value circle c
397397+ | Rect r -> Jsont.Object.Case.value rect r
398398+ in
399399+ let cases = Jsont.Object.Case.[make circle; make rect] in
400400+ Jsont.Object.map ~kind:"Geometry" make
401401+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
402402+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:type' ~enc_case cases
403403+ |> Jsont.Object.finish
404404+end
405405+]}
406406+407407+{2:cases_untagged Untagged object types}
408408+409409+Sometimes objects types are not determined by a specific {{!cases}case
410410+member} but rather by the presence or absence of certain members. In
411411+this case the easiest is to make object members optional with
412412+{!Jsont.Object.opt_mem} and sort out their presence and absence
413413+manually in the decoding function given to {!Jsont.Object.val-map}.
414414+415415+For example a response message that has always an [id] member and a
416416+[result] member in case of success and an mutually exclusive [error]
417417+member in case of error can be modelled as follows:
418418+{[
419419+module Response = struct
420420+ type t =
421421+ { id : int;
422422+ value : (Jsont.json, string) result }
423423+424424+ let make id result error =
425425+ let pp_mem = Jsont.Repr.pp_code in
426426+ match result, error with
427427+ | Some result, None -> { id; value = Ok result }
428428+ | None, Some error -> { id; value = Error error }
429429+ | Some _ , Some _ ->
430430+ Jsont.Error.msgf Jsont.Meta.none "Both %a and %a members are defined"
431431+ pp_mem "result" pp_mem "error"
432432+ | None, None ->
433433+ Jsont.Error.msgf Jsont.Meta.none "Missing either %a or %a member"
434434+ pp_mem "result" pp_mem "error"
435435+436436+ let result r = match r.value with Ok v -> Some v | Error _ -> None
437437+ let error r = match r.value with Ok _ -> None | Error e -> Some e
438438+439439+ let jsont =
440440+ Jsont.Object.map make
441441+ |> Jsont.Object.mem "id" Jsont.int ~enc:(fun r -> r.id)
442442+ |> Jsont.Object.opt_mem "result" Jsont.json ~enc:result
443443+ |> Jsont.Object.opt_mem "error" Jsont.string ~enc:error
444444+ |> Jsont.Object.finish
445445+end
446446+]}
447447+448448+A {{:https://www.jsonrpc.org/specification#response_object}JSON-RPC
449449+response object} has such a structure. A full modelling of the data
450450+JSON-RPC data format with [Jsont] can be found
451451+{{:https://erratique.ch/repos/jsont/tree/test/json_rpc.ml}here}.
452452+453453+{2:flattening Flattening nested objects}
454454+455455+If you are only interested in extracting data it may be useful to
456456+flatten some objects whose members are too nested for your needs.
457457+458458+For that just remember that nothing says that JSON objects
459459+cannot be mapped to OCaml functions. For examples to gather this kind
460460+of data for a group of person into a single record:
461461+462462+{@json[
463463+{
464464+ "info" : { "id" : 1, "name": "untitled" }
465465+ "persons" : [ … ]
466466+}
467467+]}
468468+469469+You can use the following structure:
470470+471471+{[
472472+module Group = struct
473473+ type t = { id : int; name : string; persons : Person.t list }
474474+ let make id name persons = { id; name; persons }
475475+476476+ let info_jsont =
477477+ Jsont.Object.map make
478478+ |> Jsont.Object.mem "id" Jsont.int
479479+ |> Jsont.Object.mem "name" Jsont.string
480480+ |> Jsont.Object.finish
481481+482482+ let jsont =
483483+ Jsont.Object.map (fun k persons -> k persons)
484484+ |> Jsont.Object.mem "info" info_jsont
485485+ |> Jsont.Object.mem "persons" (Jsont.list Person.jsont)
486486+ |> Jsont.Object.finish
487487+end
488488+]}
489489+490490+This however will not allow you to use [jsont] to encode. If you wish
491491+to do so it's likely better to follow the JSON structure and hide the
492492+annoying access structure under an abstract type behind a nice API.
493493+494494+{1:recursion Dealing with recursive JSON}
495495+496496+To describe recursive JSON values you need to define your description
497497+in a [lazy] expression and use {!Jsont.rec'} to refer to the value
498498+you are defining. This results in the following structure:
499499+500500+{[
501501+let jsont : t Jsont.t =
502502+ let rec t = lazy ( … Jsont.rec' t … ) in
503503+ Lazy.force t
504504+]}
505505+506506+For example a tree encoded as a JSON object with:
507507+{@json[
508508+{ "value": …,
509509+ "children": […] }
510510+]}
511511+512512+Is modelled by:
513513+514514+{[
515515+module Tree = struct
516516+ type 'a t = Node of 'a * 'a t list
517517+ let make v children = Node (v, children)
518518+ let value (Node (v, _)) = v
519519+ let children (Node (_, children)) = children
520520+ let jsont value_type =
521521+ let rec t = lazy
522522+ (Jsont.Object.map ~kind:"Tree" make
523523+ |> Jsont.Object.mem "value" value_type ~enc:value
524524+ |> Jsont.Object.mem "children" (Jsont.list (Jsont.rec' t)) ~enc:children
525525+ |> Jsont.Object.finish)
526526+ in
527527+ Lazy.force t
528528+end
529529+]}
530530+531531+The
532532+{{:https://erratique.ch/repos/jsont/tree/test/topojson.ml}[topojson.ml]}
533533+and
534534+{{:https://erratique.ch/repos/jsont/tree/test/geojson.ml}[geojson.ml]}
535535+examples in the source repository provide more extensive examples of
536536+recursive definition.
+112
vendor/opam/jsont/doc/index.mld
···11+{0 Jsont {%html: <span class="version">%%VERSION%%</span>%}}
22+33+Jsont is an OCaml library for declarative JSON data manipulation. It
44+provides:
55+66+- Combinators for describing JSON data using the OCaml values of your
77+ choice. The descriptions can be used by generic functions to
88+ decode, encode, query and update JSON data without having to
99+ construct a generic JSON representation.
1010+- A {{!Jsont_bytesrw}JSON codec} with optional text location tracking and layout
1111+ preservation. The codec is compatible with effect-based concurrency.
1212+1313+The descriptions are independent from the codec and can be used by
1414+third-party processors or codecs.
1515+1616+{1:manuals Manuals}
1717+1818+The following manuals are available.
1919+2020+{ul
2121+{- The {{!quick_start}quick start} should do so.}
2222+{- The {{!cookbook}[Jsont] cookbook} has a few conventions and JSON
2323+ data modelling recipes.}}
2424+2525+The {{:https://erratique.ch/repos/jsont/tree/test}test directory}
2626+in the source repository of Jsont has a few more examples.
2727+2828+{1:jsont Library [jsont]}
2929+3030+{!modules:Jsont}
3131+3232+{1:jsont_bytesrw Library [jsont.bytesrw]}
3333+3434+This library depends on the {!bytesrw} library and exports the [jsont] library.
3535+3636+{!modules:Jsont_bytesrw}
3737+3838+{1:jsont_brr Library [jsont.brr]}
3939+4040+This library depends on the {!brr} library and exports the [jsont] library.
4141+4242+{!modules:
4343+Jsont_brr}
4444+4545+{1:quick_start Quick start}
4646+4747+Given JSON for task items encoded in JSON as follows:
4848+{[
4949+let data =
5050+{|
5151+{ "task": "Make new release",
5252+ "status": "todo",
5353+ "tags": ["work", "softwre"] }|}
5454+]}
5555+5656+First we can correct that typo in the ["tags"] list with:
5757+5858+{[
5959+let () =
6060+ let p = Jsont.Path.(root |> mem "tags" |> nth 1) in
6161+ let update = Jsont.(set_path string p "software") in
6262+ let correct = Jsont_bytesrw.recode_string ~layout:true update data in
6363+ print_endline (Result.get_ok correct)
6464+]}
6565+6666+Now to work with the data in OCaml without pain we can model it by:
6767+6868+{[
6969+module Status = struct
7070+ type t = Todo | Done | Cancelled
7171+ let assoc = ["todo", Todo; "done", Done; "cancelled", Cancelled ]
7272+ let jsont = Jsont.enum ~kind:"Status" assoc
7373+end
7474+7575+module Item = struct
7676+ type t = { task : string; status : Status.t; tags : string list; }
7777+ let make task status tags = { task; status; tags }
7878+ let task i = i.task
7979+ let status i = i.status
8080+ let tags i = i.tags
8181+ let jsont =
8282+ Jsont.Object.map ~kind:"Item" make
8383+ |> Jsont.Object.mem "task" Jsont.string ~enc:task
8484+ |> Jsont.Object.mem "status" Status.jsont ~enc:status
8585+ |> Jsont.Object.mem "tags" Jsont.(list string) ~enc:tags
8686+ ~dec_absent:[] ~enc_omit:(( = ) [])
8787+ |> Jsont.Object.finish
8888+end
8989+]}
9090+9191+Lists of task items can be serialized to strings with, for example,
9292+{!Jsont_bytesrw}:
9393+9494+{[
9595+let items = Jsont.list Item.jsont
9696+let items_of_json s = Jsont_bytesrw.decode_string items s
9797+let items_to_json ?format is = Jsont_bytesrw.encode_string ?format items is
9898+]}
9999+100100+If you are using [js_of_ocaml] the browser's built-in JavaScript
101101+parser can be used with {!Jsont_brr} from the [jsont.brr] library:
102102+103103+{[
104104+let items_of_json s = Jsont_brr.decode items s
105105+let items_to_json is = Jsont_brr.encode items is
106106+]}
107107+108108+The {{!page-cookbook}cookbook} has more JSON modelling recipes, the
109109+{{:https://erratique.ch/repos/jsont/tree/test/topojson.ml}[topojson.ml]},
110110+{{:https://erratique.ch/repos/jsont/tree/test/geojson.ml}[geojson.ml]},
111111+{{:https://erratique.ch/repos/jsont/tree/test/json_rpc.ml}[json_rpc.ml]},
112112+in the source repository provide full examples of JSON schema modelisations.
···11+opam-version: "2.0"
22+name: "jsont"
33+synopsis: "Declarative JSON data manipulation for OCaml"
44+description: """\
55+Jsont is an OCaml library for declarative JSON data manipulation. It
66+provides:
77+88+- Combinators for describing JSON data using the OCaml values of your
99+ choice. The descriptions can be used by generic functions to
1010+ decode, encode, query and update JSON data without having to
1111+ construct a generic JSON representation.
1212+- A JSON codec with optional text location tracking and layout
1313+ preservation. The codec is compatible with effect-based concurrency.
1414+1515+The descriptions are independent from the codec and can be used by
1616+third-party processors or codecs.
1717+1818+Jsont is distributed under the ISC license. It has no dependencies.
1919+The codec is optional and depends on the [`bytesrw`] library. The JavaScript
2020+support is optional and depends on the [`brr`] library.
2121+2222+Homepage: <https://erratique.ch/software/jsont/>
2323+2424+[`bytesrw`]: https://erratique.ch/software/bytesrw
2525+[`brr`]: https://erratique.ch/software/brr"""
2626+maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>"
2727+authors: "The jsont programmers"
2828+license: "ISC"
2929+tags: ["json" "codec" "org:erratique"]
3030+homepage: "https://erratique.ch/software/jsont"
3131+doc: "https://erratique.ch/software/jsont/doc"
3232+bug-reports: "https://github.com/dbuenzli/jsont/issues"
3333+depends: [
3434+ "ocaml" {>= "4.14.0"}
3535+ "ocamlfind" {build}
3636+ "ocamlbuild" {build}
3737+ "topkg" {build & >= "1.1.0"}
3838+ "b0" {dev & with-test}
3939+]
4040+depopts: ["cmdliner" "brr" "bytesrw"]
4141+conflicts: [
4242+ "cmdliner" {< "1.3.0"}
4343+ "brr" {< "0.0.6"}
4444+]
4545+build: [
4646+ "ocaml"
4747+ "pkg/pkg.ml"
4848+ "build"
4949+ "--dev-pkg"
5050+ "%{dev}%"
5151+ "--with-cmdliner"
5252+ "%{cmdliner:installed}%"
5353+ "--with-bytesrw"
5454+ "%{bytesrw:installed}%"
5555+ "--with-brr"
5656+ "%{brr:installed}%"
5757+]
5858+dev-repo: "git+https://erratique.ch/repos/jsont.git"
5959+x-maintenance-intent: ["(latest)"]
+59
vendor/opam/jsont/opam
···11+opam-version: "2.0"
22+name: "jsont"
33+synopsis: "Declarative JSON data manipulation for OCaml"
44+description: """\
55+Jsont is an OCaml library for declarative JSON data manipulation. It
66+provides:
77+88+- Combinators for describing JSON data using the OCaml values of your
99+ choice. The descriptions can be used by generic functions to
1010+ decode, encode, query and update JSON data without having to
1111+ construct a generic JSON representation.
1212+- A JSON codec with optional text location tracking and layout
1313+ preservation. The codec is compatible with effect-based concurrency.
1414+1515+The descriptions are independent from the codec and can be used by
1616+third-party processors or codecs.
1717+1818+Jsont is distributed under the ISC license. It has no dependencies.
1919+The codec is optional and depends on the [`bytesrw`] library. The JavaScript
2020+support is optional and depends on the [`brr`] library.
2121+2222+Homepage: <https://erratique.ch/software/jsont/>
2323+2424+[`bytesrw`]: https://erratique.ch/software/bytesrw
2525+[`brr`]: https://erratique.ch/software/brr"""
2626+maintainer: "Daniel Bünzli <daniel.buenzl i@erratique.ch>"
2727+authors: "The jsont programmers"
2828+license: "ISC"
2929+tags: ["json" "codec" "org:erratique"]
3030+homepage: "https://erratique.ch/software/jsont"
3131+doc: "https://erratique.ch/software/jsont/doc"
3232+bug-reports: "https://github.com/dbuenzli/jsont/issues"
3333+depends: [
3434+ "ocaml" {>= "4.14.0"}
3535+ "ocamlfind" {build}
3636+ "ocamlbuild" {build}
3737+ "topkg" {build & >= "1.1.0"}
3838+ "b0" {dev & with-test}
3939+]
4040+depopts: ["cmdliner" "brr" "bytesrw"]
4141+conflicts: [
4242+ "cmdliner" {< "1.3.0"}
4343+ "brr" {< "0.0.6"}
4444+]
4545+build: [
4646+ "ocaml"
4747+ "pkg/pkg.ml"
4848+ "build"
4949+ "--dev-pkg"
5050+ "%{dev}%"
5151+ "--with-cmdliner"
5252+ "%{cmdliner:installed}%"
5353+ "--with-bytesrw"
5454+ "%{bytesrw:installed}%"
5555+ "--with-brr"
5656+ "%{brr:installed}%"
5757+]
5858+dev-repo: "git+https://erratique.ch/repos/jsont.git"
5959+x-maintenance-intent: ["(latest)"]
+40
vendor/opam/jsont/paper/README.md
···11+This is a [paper] written as a functional pearl about the general
22+technique used by the library. It was [rejected] by the [Journal of
33+Functional Progamming][jfp] but I don't have the time and energy to
44+significantly rewrite it (see below).
55+66+I think it's readable in its current form if you are an OCaml
77+programmer and either want to understand how the library works or to
88+apply the technique on other generic data formats.
99+1010+[paper]: soup.pdf
1111+[rejected]: jfp-reject.txt
1212+[jfp]: https://www.cambridge.org/core/journals/journal-of-functional-programming
1313+1414+1515+## Rewrite (if ever happens)
1616+1717+- Address reviewer comments and their misunderstandings.
1818+1919+ Part of the problem is that we wanted to expose a real world
2020+ blueprint of a technique, have it as a pearl and limit the
2121+ exposition to fit on 15 pages (self-imposed).
2222+2323+ Now we have the following conflicting complaints:
2424+2525+ - It's too technical and detailed for a pearl. It's not "joyful" enough.
2626+ Indeed it's a very boring exposition of the full details it takes to
2727+ have an ergonomic system in ML for dealing with the serialisation disaster
2828+ that JSON is.
2929+ - There are not enough motivating examples and details about design choices
3030+ (though honestly there's not thousands ways to construct and deconstruct
3131+ an array) and we get complaints that we do not reference related works.
3232+3333+ So if anything it should likely rather be turned into a regular
3434+ academic paper but I'm not sure it's worth the effort. The document
3535+ as it stands is likely already useful for a motivated individual.
3636+3737+- Like was eventually done in `jsont`, also have an optional
3838+ `unknowns_mems` in `Obj_cases` shapes. This makes the exposition
3939+ slightly more complex though as we need to talk about the overriding
4040+ behaviour.
+338
vendor/opam/jsont/paper/jfp-reject.txt
···11+Submitted: 2024-06-26
22+Decision: 2024-08-29
33+44+Dear Mr. Bünzli:
55+66+Manuscript ID JFP-2024-0027 entitled "An alphabet for your data soups"
77+which you submitted to the Journal of Functional Programming, has been
88+reviewed. The comments from reviewers are included at the bottom of
99+this letter.
1010+1111+In view of the criticisms of the reviewers, I must decline the
1212+manuscript for publication in the Journal of Functional Programming at
1313+this time. However, a *new* manuscript may be submitted which takes
1414+into consideration these comments.
1515+1616+Please note that resubmitting your manuscript does not guarantee
1717+eventual acceptance, and that your resubmission will be subject to
1818+re-review by the reviewers before a decision is rendered.
1919+2020+You will be unable to make your revisions on the originally submitted
2121+version of your manuscript. Instead, revise your manuscript and submit
2222+it as a new paper.
2323+2424+If you decide to resubmit, please state the manuscript number of the
2525+previous submission in your cover letter.
2626+2727+2828+Sincerely,
2929+Prof. Functional Pearls
3030+Journal of Functional Programming
3131+prof-pearls@online.de
3232+3333+Editors' Comments to Author
3434+3535+Reviewers' Comments to Author:
3636+Referee: 1
3737+3838+Comments to the Author
3939+4040+This paper presents an OCaml combinator library for converting between
4141+JSON data and ML typed values. The library may be a joy to use, but
4242+this functional pearl doesn’t show that: Concrete examples of how to
4343+use the library are scant; for instance, Section 3.5 lists three
4444+“patterns found in JSON data schemas that we want to support”, but
4545+only the first pattern is illustrated (“in Section 3”), and the “query
4646+and update” combinators in Section 5 are not shown in use at all. The
4747+library may be intellectually stimulating to build, but this
4848+functional pearl doesn’t show that: Implementation code often appears
4949+whose purpose is unclear (for instance, in Section 3.5.1).
5050+5151+I suggest the author think long and hard about what is instructive or
5252+nifty or interesting (hereafter “joyful”) about building or using this
5353+library. Then, pare down the library and the writing to only that
5454+part. For instance, if “objects as uniform key-value maps” and
5555+“objects as sums” are not joyful, then get rid of them. If query and
5656+update are not joyful, then get rid of them and remove dec_skip as
5757+well. On the other hand, if query and update are what’s joyful, then
5858+do you really need to decode and encode JSON data in order to share
5959+that joy? Finally, be sure to show what’s good with concrete examples,
6060+early and often.
6161+6262+Referee: 2
6363+6464+Comments to the Author
6565+# Summary
6666+6767+This pearl describes a richly typed eDSL to write bidirectional
6868+maps between JSON with an underlying (unenforced, potentially
6969+dynamic) format and ML values.
7070+The core of the paper is dedicated to describing at length
7171+the effort that goes into defining the GADT used to model
7272+these maps.
7373+A final section develops how one can reuse the machinery to
7474+define query & update combinators operating directly over the
7575+JSON objects.
7676+7777+# Assessment
7878+7979+I think this is interesting work however it currently feels
8080+too brutally technical and without clear design / correctness
8181+criteria to read like a pearl: « programs are fun, and they
8282+teach important programming techniques and fundamental design
8383+principle » (Jon Bentley on the definition of a programming
8484+pearl cf. https://www.cs.ox.ac.uk/people/jeremy.gibbons/pearls/)
8585+8686+I would also like to see more of a discussion of the related work.
8787+8888+# Main comments
8989+9090+## Missing examples
9191+9292+If the main goal is well motivated by the important goal of
9393+being able to program against a JSON "soup" in a structured
9494+and typed manner, each construct is poorly justified.
9595+9696+It would be nice for each section to have some small examples
9797+justifying why some of these definitions are so complex. Give
9898+us concrete instances of these structures you are describing!
9999+100100+p5: Any case
101101+This was really confusing to me at first until I understood
102102+(?) that the idea is that e.g. decoding (Number n) at type
103103+(Any m) amounts to decoding (Number n) at type (m.dec_number).
104104+I think it would be useful to sprinkle some examples here instead
105105+of just relying on "it embeds dynamic typing in our datatype".
106106+I even wonder whether it'd be useful to show the code for
107107+`decode_any` in parallel with the definition of `option`.
108108+109109+p9: Object shapes.
110110+Again this lacks motivation IMO. Give us plenty of examples
111111+showing why all of this complexity is needed!
112112+113113+The description of object cases is even more puzzling.
114114+115115+## Missing explanations
116116+117117+p7: dec_fun definition
118118+Please give a one sentence definitition of type ids so that
119119+we don't need to lookup the ocaml docs just to understand what
120120+they are. Looking at the code for `Dict.t`, it seems to be a
121121+unique `int` allowing you to test type equality.
122122+123123+AFAIU this means all the arguments need to have different
124124+types. Why is that a sensible assumption?
125125+126126+But looking at obj_mem, `Type.Id.make ()` seems to suggest
127127+it's not in fact a unique ID per type but rather an ID for
128128+something that happens to have this type.
129129+130130+Again, this would be a lot easier to understand with a
131131+proper explanation from the get go.
132132+133133+## Correction of type description
134134+135135+Given that the typed description induces an encoder and a
136136+decoder, it would be nice to have a correctness theorem.
137137+I suspect there is no hope to get `encode . decode = id`
138138+(multiple possible representations of the same value) but
139139+we definitely want to have `decode . encode = id`.
140140+141141+Correspondingly, I feel the presentation is missing the
142142+precise characterisation of the invariants we expect the
143143+users to respect. Ideally these could be expressed as
144144+properties testable using something like quickcheck.
145145+146146+In particular, this means specifying:
147147+148148+p4: Map case
149149+define "bidirectional map" more precisely: what sort of
150150+properties do you expect? E.g. `dec` being a partial inverse
151151+to `enc` but not the other way around? More than that? Less?
152152+153153+p5: Array case
154154+Again here it'd be nice to have a property you expect to
155155+hold for the component to be well behaved. I would expect
156156+something along the lines of:
157157+`dec_finish (enc (\ b, elt -> dec_add b ??? elt) dec_empty arr) = arr`
158158+except that `dec_add` takes an index which is not available from
159159+inside the `enc` fold.
160160+161161+## Missing related work section
162162+163163+You cited pickler combinators and alluded to generic programming
164164+but I think it would be interesting to discuss more extensively
165165+the fairly important tradition of writing "invertible parsers",
166166+"bidirectional programs", "partial isomorphisms", etc. to
167167+obtain pairs of a parser and a pretty printer e.g.
168168+169169+* There and back again: arrows for invertible programming by Alimarine
170170+* Invertible syntax descriptions: unifying parsing and pretty printing by Rendel
171171+* Correct-by-construction pretty-printing by Nils Anders Danielsson
172172+* Generic packet descriptions by Van Geest
173173+* FliPpr: A System for Deriving Parsers from Pretty-Printers by Matsuda
174174+175175+Some of these (Generic packet descriptions) include types of
176176+formats that your approach cannot handle (cf. next point)
177177+178178+The query & update section naturally brings up the (unexplored?)
179179+relationship to lenses & prisms.
180180+181181+182182+## Missing discussion of possible extension
183183+184184+It would be interesting to have a discussion of some
185185+features of common format specifications that are not
186186+tackled by the current work.
187187+188188+E.g. some formats specify
189189+- *computed* fields e.g. checksums, or
190190+- *constrained* fields e.g. a payload whose length is specified
191191+in another field.
192192+193193+Could these be accommodated? Or do you need to move to
194194+a more powerful type system like in 'Generic packet
195195+descriptions' by Van Geest mentioned above?
196196+197197+# Minor issues
198198+199199+p3: Typed representation
200200+"laziest readers" -> find better wording (or is that meant
201201+to be a pun for readers using a lazy ML?)
202202+203203+p5: Array case
204204+Given that skip & add take an index, is it worth
205205+adding a type alias `type index = int` to suggest
206206+it's meant to be a non-negative number?
207207+208208+209209+Referee: 3
210210+211211+Comments to the Author
212212+213213+---- Summary ----
214214+215215+This paper describes a library for working with JSON data. The key idea
216216+is not to describe JSON directly (as in Section 2), but rather to define
217217+a GADT describing the conversion between some OCaml type a and its JSON
218218+representation (Section 3). Given such a description, it is easy to
219219+define the actual encoding/decoding with respect to JSON and functions
220220+to query/update JSON data.
221221+222222+---- Review ----
223223+224224+Conversion between algebraic datatypes and JSON is a well studied
225225+problem: there are 100+ OCaml packages and 300+ Haskell packages for
226226+interfacing with (some form of) JSON data. It is certainly an
227227+interesting and important problem.
228228+229229+My main concern with this paper is that it descibes a solution (the GADT
230230+is Section 3), without explicitly introducing the problem and motivating
231231+the underlying design choices that lead to this solution. This is very
232232+important, not only because there is already so much work in this area,
233233+but this distinguishes a research paper from a library documentation.
234234+Especially for a pearl, I am keen to read the _ideas_ that (naturally)
235235+lead to this solution and not just the code that makes things tick.
236236+237237+Let me illustrate this point with a few examples:
238238+239239+* the GADT jsont has a separate constructor for 'map' -- essentially
240240+used to map a conversion over another GADT value. This seems rather
241241+arbitrary: was it necessary for important examples? Could there be an
242242+alternative GADT that supports map as a defined operation, rather than
243243+a separate constructor?
244244+245245+* Similarly, the case for arrays, given by the 'array_map' record, has
246246+several functions to build and convert arrays. Why choose _these_
247247+functions? Could there be others that are also useful? What
248248+considerations lead to these primitives?
249249+250250+* The base_map type has conversions between a and b -- can these fail?
251251+What (roundtrip) properties should they satisfy? There is an obvious
252252+relation with lensest that should be mentioned at the very least.
253253+254254+* The most elaborate case is that for objects. Here the design is
255255+pragmatic -- motivated by several typical use cases for objects (given
256256+at the beginning of section 3.5). Once again, I managed to read along
257257+with the code, but the principles that lead to this solution are left
258258+implicit: why does mem_map have precisely these fields? The key
259259+(heterogeneous) dictionary is in the appendix -- but what purpose does
260260+'a Type.Id.t serve? The 'apply_dict' function can still fail
261261+dynamically using Option.get -- is this a problem? If the library aims
262262+to provide more (type) safety than working with JSON directly, these
263263+issues and design choices need to be more carefully discussed.
264264+265265+This last point also shows up in section 5, where the various update and
266266+delete functions can all still throw type errors; similarly, the
267267+handling of the Any type (section 3.4) seems arbitrary and prone to
268268+dynamic failure again. If there are so many places where (type) unsafety
269269+can still sneak in -- what is achieved by the proposed solution? Could
270270+these limitations be addressed by more fancy type features? And what
271271+trade-off lead to this particular design?
272272+273273+I would strongly recommend Simon Peyton Jones' talk on "How to write
274274+great research paper" -- one of the key points it to try to convey the
275275+main ideas; the implementation should follow naturally.
276276+277277+The current introduction was not helpful in positioning the paper. Many
278278+of the points made about 'this datatype' don't make much sense on first
279279+reading -- I haven't seen the datatype yet and found it very hard to
280280+appreciate these contributions. It would be very helpful to formulate
281281+the design goals (and limitations!), independently of the actual
282282+implementation. Making these concrete by means of examples would really
283283+help -- phrases such as 'partially modelled data schemas', 'datatype
284284+generic representations' or 'generic representation of the data model'
285285+do not have much meaning without more context.
286286+287287+A good pearl does not need to exhaustively discuss related work, but
288288+there are plenty of other papers and libraries that tackle similar
289289+issues, including but not limited to the view-update problem (and
290290+lenses), other (generic programming) solutions to JSON
291291+encoding/decoding, and the many other libraries that tackle the same
292292+issue.
293293+294294+This work and these ideas may yet lead to an interesting pearl, but the
295295+article in its current form is not yet ready for publication.
296296+297297+---- Typos / minor suggestions ----
298298+299299+* General, there are no line numbers. The JFP style file requires these
300300+for submissions -- they would make giving specific feedback much
301301+easier.
302302+303303+* page 1 - 'directly on their own type system' -- I believe this is not
304304+a property of dynamic languages in general, but rather the way
305305+Javascript/Python support objects. Try to be more specific here.
306306+307307+* page 2 - use endashes (--) surrounded by spaces (the JFP default); or
308308+emdashes (---) without spaces, but never mix these style.
309309+310310+* page 2 - it would be useful to give an example of the Json struct --
311311+and illustrate why this solution is unsatisfactory to more clearly
312312+motivate the solution presented in the next section.
313313+314314+* page 3 - I know enough OCaml to get by, but what does ~enc:content
315315+mean? Why is the twiddle necessary here?
316316+317317+* page 4 - 'mapping unit values with m' sounds like m is a function,
318318+while it isn't!
319319+320320+* page 4 - 'answer is rather negative' sounds odd, perhaps 'this is not the
321321+case'? And if it isn't the case,
322322+323323+* page 4 - 'it is not directly evident in our simpler exposition...' -
324324+this feels like a rather weak argument. I can understand the
325325+importance of simplifying code for the sake of presentation -- but
326326+apparently there are other design considerations at play that have not
327327+yet been mentioned.
328328+329329+* page 6 - sentences like 'the JSON type json which maps any JSON value'
330330+indicate that there may be a need for better terminology here.
331331+332332+* page 6 ' retaining efficient decodes' grammar - perhaps 'efficient decoding'?
333333+334334+* page 7 - contructor -> constructor
335335+336336+* 'must type as defined by the object map otherwise the decode errors' -
337337+grammar -> 'all definitions must be typed in accordance with the
338338+object map otherwise the decoding fails.'
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: CC0-1.0
44+ ---------------------------------------------------------------------------*)
55+66+(* Definitions from the soup.tex paper *)
77+88+module Type = struct (* Can be deleted with OCaml >= 5.1 *)
99+ type (_, _) eq = Equal : ('a, 'a) eq
1010+ module Id = struct
1111+ type _ id = ..
1212+ module type ID = sig type t type _ id += Id : t id end
1313+ type 'a t = (module ID with type t = 'a)
1414+1515+ let make (type a) () : a t =
1616+ (module struct type t = a type _ id += Id : t id end)
1717+1818+ let provably_equal
1919+ (type a b) ((module A) : a t) ((module B) : b t) : (a, b) eq option
2020+ =
2121+ match A.Id with B.Id -> Some Equal | _ -> None
2222+2323+ let uid (type a) ((module A) : a t) =
2424+ Obj.Extension_constructor.id (Obj.Extension_constructor.of_val A.Id)
2525+ end
2626+end
2727+2828+module String_map = Map.Make (String)
2929+3030+(* Generic representation *)
3131+3232+module Json = struct
3333+ type t =
3434+ | Null of unit | Bool of bool | Number of float | String of string
3535+ | Array of t list | Obj of obj and obj = mem list and mem = string * t
3636+end
3737+3838+(* The finally tagged datatype *)
3939+4040+type ('ret, 'f) dec_fun =
4141+| Dec_fun : 'f -> ('ret, 'f) dec_fun
4242+| Dec_app : ('ret, 'a -> 'b) dec_fun * 'a Type.Id.t -> ('ret, 'b) dec_fun
4343+4444+type ('a, 'b) base_map = { dec : 'a -> 'b; enc : 'b -> 'a; }
4545+4646+type _ jsont =
4747+| Null : (unit, 'b) base_map -> 'b jsont
4848+| Bool : (bool, 'b) base_map -> 'b jsont
4949+| Number : (float, 'b) base_map -> 'b jsont
5050+| String : (string, 'b) base_map -> 'b jsont
5151+| Array : ('a, 'elt, 'builder) array_map -> 'a jsont
5252+| Obj : ('o, 'o) obj_map -> 'o jsont
5353+| Any : 'a any_map -> 'a jsont
5454+| Map : ('a, 'b) map -> 'b jsont
5555+| Rec : 'a jsont Lazy.t -> 'a jsont
5656+5757+and ('array, 'elt, 'builder) array_map =
5858+{ elt : 'elt jsont;
5959+ dec_empty : 'builder;
6060+ dec_skip : 'builder -> int -> bool;
6161+ dec_add : 'builder -> int -> 'elt -> 'builder;
6262+ dec_finish : 'builder -> 'array;
6363+ enc : 'acc. ('acc -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc; }
6464+6565+and ('o, 'dec) obj_map =
6666+{ dec : ('o, 'dec) dec_fun;
6767+ mem_decs : mem_dec String_map.t;
6868+ mem_encs : 'o mem_enc list;
6969+ shape : 'o obj_shape; }
7070+7171+and mem_dec = Mem_dec : ('o, 'a) mem_map -> mem_dec
7272+and 'o mem_enc = Mem_enc : ('o, 'a) mem_map -> 'o mem_enc
7373+and ('o, 'a) mem_map =
7474+{ name : string;
7575+ type' : 'a jsont;
7676+ id : 'a Type.Id.t;
7777+ dec_absent : 'a option;
7878+ enc : 'o -> 'a;
7979+ enc_omit : 'a -> bool; }
8080+8181+and 'o obj_shape =
8282+| Obj_basic : ('o, 'mems, 'builder) unknown_mems -> 'o obj_shape
8383+| Obj_cases : ('o, 'cases, 'tag) obj_cases -> 'o obj_shape
8484+8585+and ('o, 'mems, 'builder) unknown_mems =
8686+| Unknown_skip : ('o, unit, unit) unknown_mems
8787+| Unknown_error : ('o, unit, unit) unknown_mems
8888+| Unknown_keep :
8989+ ('mems, 'a, 'builder) mems_map * ('o -> 'mems) ->
9090+ ('o, 'mems, 'builder) unknown_mems
9191+9292+and ('mems, 'a, 'builder) mems_map =
9393+{ mems_type : 'a jsont;
9494+ id : 'mems Type.Id.t;
9595+ dec_empty : 'builder;
9696+ dec_add : string -> 'a -> 'builder -> 'builder;
9797+ dec_finish : 'builder -> 'mems;
9898+ enc : 'acc. (string -> 'a -> 'acc -> 'acc) -> 'mems -> 'acc -> 'acc }
9999+100100+and ('o, 'cases, 'tag) obj_cases =
101101+{ tag : ('o, 'tag) mem_map; (* 'o is irrelevant, 'tag is not stored *)
102102+ tag_compare : 'tag -> 'tag -> int;
103103+ id : 'cases Type.Id.t;
104104+ cases : ('cases, 'tag) case list;
105105+ enc : 'o -> 'cases;
106106+ enc_case : 'cases -> ('cases, 'tag) case_value; }
107107+108108+and ('cases, 'tag) case =
109109+| Case : ('cases, 'case, 'tag) case_map -> ('cases, 'tag) case
110110+111111+and ('cases, 'case, 'tag) case_map =
112112+{ tag : 'tag;
113113+ obj_map : ('case, 'case) obj_map;
114114+ dec : 'case -> 'cases; }
115115+116116+and ('cases, 'tag) case_value =
117117+| Case_value :
118118+ ('cases, 'case, 'tag) case_map * 'case -> ('cases, 'tag) case_value
119119+120120+and 'a any_map =
121121+{ dec_null : 'a jsont option;
122122+ dec_bool : 'a jsont option;
123123+ dec_number : 'a jsont option;
124124+ dec_string : 'a jsont option;
125125+ dec_array : 'a jsont option;
126126+ dec_obj : 'a jsont option;
127127+ enc : 'a -> 'a jsont; }
128128+129129+and ('a, 'b) map =
130130+{ dom : 'a jsont;
131131+ map : ('a, 'b) base_map }
132132+133133+(* Errors *)
134134+135135+let type_error () = failwith "type error"
136136+let unexpected_member n = failwith ("Unexpected member " ^ n)
137137+let missing_member n = failwith ("Missing member " ^ n)
138138+let unknown_case_tag () = failwith "Unknown case tag"
139139+140140+(* Any examples *)
141141+142142+let option : 'a jsont -> 'a option jsont = fun t ->
143143+ let none = Null { dec = Fun.const None; enc = Fun.const () } in
144144+ let some = Map { dom = t; map = {dec = Option.some; enc = Option.get}}in
145145+ let enc = function None -> none | Some _ -> some in
146146+ let none = Some none and some = Some some in
147147+ Any { dec_null = none; dec_bool = some; dec_number = some;
148148+ dec_string = some; dec_array = some; dec_obj = some; enc; }
149149+150150+let json : Json.t jsont = (* left as an exercise in the paper *)
151151+ let null =
152152+ Null { dec = (fun () -> Json.Null ());
153153+ enc = (function Json.Null () -> () | j -> type_error ()) }
154154+ in
155155+ let bool =
156156+ Bool { dec = (fun b -> Json.Bool b);
157157+ enc = (function Json.Bool b -> b | j -> type_error ()) }
158158+ in
159159+ let number =
160160+ Number { dec = (fun n -> Json.Number n);
161161+ enc = (function Json.Number n -> n | j -> type_error ()) }
162162+ in
163163+ let string =
164164+ String { dec = (fun s -> Json.String s);
165165+ enc = (function Json.String s -> s | j -> type_error ()) }
166166+ in
167167+ let rec array =
168168+ let dec_empty = [] and dec_add a _i v = v :: a in
169169+ let dec_finish elts = Json.Array (List.rev elts) in
170170+ let dec_skip _ _ = false in
171171+ let enc f acc = function
172172+ | Json.Array vs -> List.fold_left f acc vs | _ -> type_error ()
173173+ in
174174+ Array { elt = Rec json; dec_empty; dec_add; dec_skip; dec_finish; enc }
175175+ and obj =
176176+ let mems_id = Type.Id.make () in
177177+ let mems =
178178+ let dec_empty = [] in
179179+ let dec_add n v ms = (n, v) :: ms in
180180+ let dec_finish ms = Json.Obj (List.rev ms) in
181181+ let enc f j acc = match j with
182182+ | Json.Obj ms -> List.fold_left (fun acc (n, v) -> f n v acc) acc ms
183183+ | _ -> type_error ()
184184+ in
185185+ { mems_type = Rec json; id = mems_id; dec_empty; dec_add; dec_finish; enc}
186186+ in
187187+ Obj { dec = Dec_app (Dec_fun Fun.id, mems_id);
188188+ mem_decs = String_map.empty; mem_encs = [];
189189+ shape = Obj_basic (Unknown_keep (mems, Fun.id)) }
190190+ and json =
191191+ let enc = function
192192+ | Json.Null _ -> null | Json.Bool _ -> bool | Json.Number _ -> number
193193+ | Json.String _ -> string | Json.Array _ -> array | Json.Obj _ -> obj
194194+ in
195195+ lazy (Any { dec_null = Some null; dec_bool = Some bool;
196196+ dec_number = Some number; dec_string = Some string;
197197+ dec_array = Some array; dec_obj = Some obj; enc })
198198+ in
199199+ Lazy.force json
200200+201201+(* Heterogeneous key-value maps *)
202202+203203+module Dict = struct
204204+ module M = Map.Make (Int)
205205+ type binding = B : 'a Type.Id.t * 'a -> binding
206206+ type t = binding M.t
207207+ let empty = M.empty
208208+ let add k v m = M.add (Type.Id.uid k) (B (k, v)) m
209209+ let find : type a. a Type.Id.t -> t -> a option =
210210+ fun k m -> match M.find_opt (Type.Id.uid k) m with
211211+ | None -> None
212212+ | Some B (k', v) ->
213213+ match Type.Id.provably_equal k k' with
214214+ | Some Type.Equal -> Some v | None -> assert false
215215+end
216216+217217+type ('ret, 'f) app =
218218+| Fun : 'f -> ('ret, 'f) app
219219+| App : ('ret, 'a -> 'b) app * 'a -> ('ret, 'b) app
220220+221221+let rec apply_dict : type ret f. (ret, f) dec_fun -> Dict.t -> f =
222222+fun dec dict -> match dec with
223223+| Dec_fun f -> f
224224+| Dec_app (f,arg) -> (apply_dict f dict) (Option.get (Dict.find arg dict))
225225+226226+(* Decode *)
227227+228228+let rec decode : type a. a jsont -> Json.t -> a =
229229+fun t j -> match t with
230230+| Null map -> (match j with Json.Null v -> map.dec v | _ -> type_error ())
231231+| Bool map -> (match j with Json.Bool b -> map.dec b | _ -> type_error ())
232232+| Number map ->
233233+ (match j with
234234+ | Json.Number n -> map.dec n | Json.Null _ -> map.dec Float.nan
235235+ | _ -> type_error ())
236236+| String map -> (match j with Json.String s -> map.dec s | _ -> type_error ())
237237+| Array map ->
238238+ (match j with Json.Array vs -> decode_array map vs | j -> type_error ())
239239+| Obj map ->
240240+ (match j with Json.Obj mems -> decode_obj map mems | j -> type_error ())
241241+| Map map -> map.map.dec (decode map.dom j)
242242+| Any map -> decode_any t map j
243243+| Rec t -> decode (Lazy.force t) j
244244+245245+and decode_array : type a e b. (a, e, b) array_map -> Json.t list -> a =
246246+fun map vs ->
247247+ let add (i, a) v =
248248+ i + 1, (if map.dec_skip a i then a else map.dec_add a i (decode map.elt v))
249249+ in
250250+ map.dec_finish (snd (List.fold_left add (0, map.dec_empty) vs))
251251+252252+and decode_obj : type o. (o, o) obj_map -> Json.obj -> o =
253253+fun map mems ->
254254+ apply_dict map.dec @@
255255+ decode_obj_map map String_map.empty String_map.empty Dict.empty mems
256256+257257+and decode_obj_map : type o.
258258+ (o, o) obj_map -> mem_dec String_map.t -> mem_dec String_map.t -> Dict.t ->
259259+ Json.obj -> Dict.t
260260+=
261261+fun map mem_miss mem_decs dict mems ->
262262+ let u n _ _ = invalid_arg (n ^ "member defined twice") in
263263+ let mem_miss = String_map.union u mem_miss map.mem_decs in
264264+ let mem_decs = String_map.union u mem_decs map.mem_decs in
265265+ match map.shape with
266266+ | Obj_cases cases -> decode_obj_case cases mem_miss mem_decs dict [] mems
267267+ | Obj_basic u ->
268268+ match u with
269269+ | Unknown_skip -> decode_obj_basic u () mem_miss mem_decs dict mems
270270+ | Unknown_error -> decode_obj_basic u () mem_miss mem_decs dict mems
271271+ | Unknown_keep (map, _) ->
272272+ decode_obj_basic u map.dec_empty mem_miss mem_decs dict mems
273273+274274+and decode_obj_basic : type o map builder.
275275+ (o, map, builder) unknown_mems -> builder -> mem_dec String_map.t ->
276276+ mem_dec String_map.t -> Dict.t -> Json.obj -> Dict.t
277277+=
278278+fun u umap mem_miss mem_decs dict -> function
279279+| [] ->
280280+ let dict = match u with
281281+ | Unknown_skip | Unknown_error -> dict
282282+ | Unknown_keep (map, _) -> Dict.add map.id (map.dec_finish umap) dict
283283+ in
284284+ let add_default _ (Mem_dec m) dict = match m.dec_absent with
285285+ | Some v -> Dict.add m.id v dict | None -> missing_member m.name
286286+ in
287287+ String_map.fold add_default mem_miss dict
288288+| (n, v) :: mems ->
289289+ match String_map.find_opt n mem_decs with
290290+ | Some (Mem_dec m) ->
291291+ let dict = Dict.add m.id (decode m.type' v) dict in
292292+ let mem_miss = String_map.remove n mem_miss in
293293+ decode_obj_basic u umap mem_miss mem_decs dict mems
294294+ | None ->
295295+ match u with
296296+ | Unknown_skip -> decode_obj_basic u umap mem_miss mem_decs dict mems
297297+ | Unknown_error -> unexpected_member n
298298+ | Unknown_keep (map, _) ->
299299+ let umap = map.dec_add n (decode map.mems_type v) umap in
300300+ decode_obj_basic u umap mem_miss mem_decs dict mems
301301+302302+and decode_obj_case : type o cases tag.
303303+ (o, cases, tag) obj_cases -> mem_dec String_map.t -> mem_dec String_map.t ->
304304+ Dict.t -> Json.obj -> Json.obj -> Dict.t
305305+=
306306+fun cases mem_miss mem_decs dict delay mems ->
307307+ let decode_case_tag tag =
308308+ let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
309309+ match List.find_opt eq_tag cases.cases with
310310+ | None -> unknown_case_tag ()
311311+ | Some (Case case) ->
312312+ let mems = List.rev_append delay mems in
313313+ let dict = decode_obj_map case.obj_map mem_miss mem_decs dict mems in
314314+ Dict.add cases.id (case.dec (apply_dict case.obj_map.dec dict)) dict
315315+ in
316316+ match mems with
317317+ | [] ->
318318+ (match cases.tag.dec_absent with
319319+ | Some t -> decode_case_tag t | None -> missing_member cases.tag.name)
320320+ | (n, v as mem) :: mems ->
321321+ if n = cases.tag.name then decode_case_tag (decode cases.tag.type' v) else
322322+ match String_map.find_opt n mem_decs with
323323+ | None -> decode_obj_case cases mem_miss mem_decs dict (mem :: delay) mems
324324+ | Some (Mem_dec m) ->
325325+ let dict = Dict.add m.id (decode m.type' v) dict in
326326+ let mem_miss = String_map.remove n mem_miss in
327327+ decode_obj_case cases mem_miss mem_decs dict delay mems
328328+329329+and decode_any : type a. a jsont -> a any_map -> Json.t -> a =
330330+fun t map j ->
331331+ let dec t m j = match m with Some t -> decode t j | None -> type_error () in
332332+ match j with
333333+ | Json.Null _ -> dec t map.dec_null j
334334+ | Json.Bool _ -> dec t map.dec_bool j
335335+ | Json.Number _ -> dec t map.dec_number j
336336+ | Json.String _ -> dec t map.dec_string j
337337+ | Json.Array _ -> dec t map.dec_array j
338338+ | Json.Obj _ -> dec t map.dec_obj j
339339+340340+(* Encode *)
341341+342342+let rec encode : type a. a jsont -> a -> Json.t =
343343+fun t v -> match t with
344344+| Null map -> Json.Null (map.enc v)
345345+| Bool map -> Json.Bool (map.enc v)
346346+| Number map -> Json.Number (map.enc v)
347347+| String map -> Json.String (map.enc v)
348348+| Array map ->
349349+ let encode_elt a elt = (encode map.elt elt) :: a in
350350+ Json.Array (List.rev (map.enc encode_elt [] v))
351351+| Obj map -> Json.Obj (List.rev (encode_obj map v []))
352352+| Any map -> encode (map.enc v) v
353353+| Map map -> encode map.dom (map.map.enc v)
354354+| Rec t -> encode (Lazy.force t) v
355355+356356+and encode_obj : type o. (o, o) obj_map -> o -> Json.obj -> Json.obj =
357357+fun map o obj ->
358358+ let encode_mem obj (Mem_enc map) =
359359+ let v = map.enc o in
360360+ if map.enc_omit v then obj else (map.name, encode map.type' v) :: obj
361361+ in
362362+ let obj = List.fold_left encode_mem obj map.mem_encs in
363363+ match map.shape with
364364+ | Obj_basic (Unknown_keep (map, enc)) ->
365365+ let encode_mem n v obj = (n, encode map.mems_type v) :: obj in
366366+ map.enc encode_mem (enc o) obj
367367+ | Obj_basic _ -> obj
368368+ | Obj_cases cases ->
369369+ let Case_value (case, c) = cases.enc_case (cases.enc o) in
370370+ let obj =
371371+ if cases.tag.enc_omit case.tag then obj else
372372+ (cases.tag.name, encode cases.tag.type' case.tag) :: obj
373373+ in
374374+ encode_obj case.obj_map c obj
375375+376376+(* Object construction *)
377377+378378+let obj_mem :
379379+ string -> 'a jsont -> enc:('o -> 'a) ->
380380+ ('o, 'a -> 'b) obj_map -> ('o, 'b) obj_map
381381+=
382382+fun name type' ~enc obj_map ->
383383+ let id = Type.Id.make () in
384384+ let dec_absent = None and enc_omit = Fun.const false in
385385+ let mm = { name; type'; id; dec_absent; enc; enc_omit } in
386386+ let dec = Dec_app (obj_map.dec, mm.id) in
387387+ let mem_decs = String_map.add mm.name (Mem_dec mm) obj_map.mem_decs in
388388+ let mem_encs = Mem_enc mm :: obj_map.mem_encs in
389389+ { obj_map with dec; mem_decs; mem_encs; }
390390+391391+let bool = Bool { dec = Fun.id; enc = Fun.id }
392392+let string = String { dec = Fun.id; enc = Fun.id }
393393+let obj_finish o = Obj { o with mem_encs = List.rev o.mem_encs }
394394+let obj_map : 'dec -> ('o, 'dec) obj_map = fun make ->
395395+ let dec = Dec_fun make and shape = Obj_basic Unknown_skip in
396396+ { dec; mem_decs = String_map.empty; mem_encs = []; shape }
397397+398398+module Message = struct
399399+ type t = { content : string; public : bool }
400400+ let make content public = { content; public }
401401+ let content msg = msg.content
402402+ let public msg = msg.public
403403+ let jsont : t jsont =
404404+ obj_map make
405405+ |> obj_mem "content" string ~enc:content
406406+ |> obj_mem "public" bool ~enc:public
407407+ |> obj_finish
408408+end
409409+410410+(* Queries and updates *)
411411+412412+type 'a query = 'a jsont
413413+let query : 'a query -> Json.t -> 'a = decode
414414+415415+let get_mem : string -> 'a query -> 'a query = fun name q ->
416416+ obj_map Fun.id |> obj_mem name q ~enc:Fun.id |> obj_finish
417417+418418+let get_nth : int -> 'a query -> 'a query = fun nth q ->
419419+ let dec_empty = None and dec_add _ _ v = Some v in
420420+ let dec_skip _ k = nth <> k in
421421+ let dec_finish = function None -> failwith "too short" | Some v -> v in
422422+ let enc f acc v = f acc v (* Singleton array with the query result *) in
423423+ Array { elt = q; dec_empty; dec_add; dec_skip; dec_finish; enc }
424424+425425+let update_mem : string -> 'a jsont -> Json.t jsont = fun name q ->
426426+ let dec = function
427427+ | Json.Obj ms ->
428428+ let update (n, v as m) =
429429+ if n = name then (n, encode q (decode q v)) else m
430430+ in
431431+ Json.Obj (List.map update ms)
432432+ | _ -> failwith "type error"
433433+ in
434434+ Map { dom = json; map = { dec; enc = Fun.id } }
435435+436436+let delete_mem : string -> Json.t query = fun name ->
437437+ let dec = function
438438+ | Json.Obj ms -> Json.Obj (List.filter (fun (n, _) -> n <> name) ms)
439439+ | _ -> type_error ()
440440+ in
441441+ Map { dom = json; map = { dec; enc = Fun.id } }
442442+443443+let const : 'a jsont -> 'a -> 'a jsont = fun t v ->
444444+ let dec _ = v and enc _ = encode t v in
445445+ Map { dom = json; map = { dec; enc } }
446446+447447+(* Implementations not in the paper *)
448448+449449+let map : ('a -> 'b) -> ('b -> 'a) -> 'a jsont -> 'b jsont =
450450+fun f g t -> Map { dom = t; map = { dec = f; enc = g }}
451451+452452+let update_nth : int -> 'a jsont -> Json.t jsont = fun nth q ->
453453+ let dec = function
454454+ | Json.Array vs ->
455455+ let update i v = if i = nth then encode q (decode q v) else v in
456456+ Json.Array (List.mapi update vs)
457457+ | _ -> failwith "type error"
458458+ in
459459+ Map { dom = json; map = { dec; enc = Fun.id } }
460460+461461+let delete_nth : int -> Json.t query = fun nth ->
462462+ let dec = function
463463+ | Json.Array vs ->
464464+ let add (i, acc) v = i + 1, (if i = nth then acc else v :: acc) in
465465+ Json.Array (List.rev (snd (List.fold_left add (0, []) vs)))
466466+ | _ -> type_error ()
467467+ in
468468+ Map { dom = json; map = { dec; enc = Fun. id }}
vendor/opam/jsont/paper/soup.pdf
This is a binary file and will not be displayed.
+1115
vendor/opam/jsont/paper/soup.tex
···11+\documentclass[nolinenum]{jfp}
22+\usepackage[T1]{fontenc}
33+\usepackage[scaled=0.9]{beramono}
44+\usepackage{graphicx}
55+\usepackage{listings}
66+\usepackage{hyperref}
77+\usepackage{relsize}
88+\usepackage{multicol}
99+1010+\begin{document}
1111+1212+\lstset{
1313+ columns=[c]fixed ,
1414+ basicstyle=\relscale{0.85}\ttfamily\linespread{1.1}\selectfont,
1515+ keywordstyle=\bfseries,
1616+ mathescape=true,
1717+ upquote=true,
1818+ commentstyle=\slshape,
1919+ breaklines=true,
2020+ showstringspaces=false}
2121+2222+\lstdefinelanguage{ocaml}{language=[objective]caml,
2323+ % Fixes double quotes in char literals
2424+ literate={'"'}{\textquotesingle "\textquotesingle}3
2525+ {'\\"'}{\textquotesingle \textbackslash"\textquotesingle}4
2626+ {;}{\textbf{;}}1
2727+ {|}{\textbf{|}}1
2828+ {type'}{type'}5 % gets rid of bold
2929+}
3030+3131+%%\journaltitle{JFP}
3232+%%\cpr{Cambridge University Press}
3333+%%\doival{10.1017/xxxxx}
3434+%\righttitle{Journal of Functional Programming}
3535+\righttitle{}
3636+\journaltitle{\textbf{WARNING, REJECTED by the JFP}
3737+ but may still be useful}
3838+\cpr{Daniel C. Bünzli}
3939+\doival{xx.xxxx/xxxxx Document style kept for now, because layout
4040+ was tailored for it. revision 1}
4141+4242+\newcommand{\thetitle}{An alphabet for your data soups}
4343+4444+\title{\thetitle}
4545+\lefttitle{\thetitle}
4646+4747+\totalpg{\pageref{lastpage01}}
4848+\jnlDoiYr{2024}
4949+5050+\newcommand{\json}{\textsc{json}}
5151+\newcommand{\ocaml}{\textsc{ocaml}}
5252+\newcommand{\ml}{\textsc{ml}}
5353+\newcommand{\code}[1]{{\lstinline[language=ocaml]{#1}}}
5454+5555+\begin{authgrp}
5656+ \renewcommand*{\thefootnote}{\fnsymbol{footnote}}
5757+ \author{Daniel C. Bünzli}%
5858+ \footnote{Funded by the Swiss National Science
5959+ Foundation (grant \oldstylenums{\textsc{pp00p1\_211010}},\emph{
6060+ The Epistemology of Climate Change –– Understanding the Climate Challenge})
6161+ and a grant from the
6262+ \href{https://ocaml-sf.org/}{OCaml Software Foundation}.
6363+ }%
6464+ \affiliation{Institute of Philosophy, University of Bern, Switzerland\\
6565+ (\email{daniel.buenzli@erratique.ch})}
6666+6767+\end{authgrp}
6868+6969+\begin{abstract}
7070+ Dealing with ubiquitous but poorly typed and structured data models
7171+ like \json{} in \ml{} languages is unpleasant. But it doesn't have
7272+ to be. We show how to define a generalized algebraic datatype whose
7373+ values denote bidirectional maps between subsets of values of a data
7474+ model and the \ml{} values of your choice. With suitable combinators
7575+ these maps are quick and pleasant to define in a declarative
7676+ style. The result can be used by generic functions that decode,
7777+ encode, query and update data soups with nicely typed values.
7878+\end{abstract}
7979+8080+\maketitle[F]
8181+\section{Introduction}
8282+8383+Processing generic data models like \json{} in \ml{} languages is
8484+unpleasant. \ml{} values can be converted to these data models with
8585+pickler combinators \citep{picklers} or datatype-generic programming
8686+techniques \citep{datatypegeneric}. However, partially or fully
8787+modelling \emph{their} data schemas remains cumbersome. Using a
8888+generic type for the data model works well in dynamically typed
8989+languages because it directly maps on their own type systems. But in
9090+\ml{} this representation is unnatural and frustrating to use.
9191+9292+Instead, we show how to define a generalized algebraic datatype whose
9393+values denote bidirectional maps between subsets of values of the data
9494+model and the \ml{} values you want to use. With appropriate
9595+combinators to construct them, these descriptions can be made quick to
9696+define in the \emph{decoding} and \emph{encoding} direction possibly
9797+eliding one if not immediately useful. The values of this datatype can
9898+be used by generic functions that:
9999+100100+\begin{itemize}
101101+\item Directly decode or encode the data model to the \ml{} values of
102102+ your choice without constructing values of a generic
103103+ representation of the data model.\footnote{For
104104+ \json{}, since the shape of an object may depend on one of its
105105+ members and that members are unordered, some form of generic
106106+ buffering may be needed to decode certain data schemas.}
107107+\item Query and update data of partially modelled data schemas with
108108+ arbitrary \ml{} values.
109109+\item Automatically construct them from other datatype-generic
110110+ representations you may already have defined for your \ml{} types.
111111+\end{itemize}
112112+113113+Like pickler combinators \citep{picklers}, the definition of these
114114+values can be made pleasantly declarative. The decoding and encoding
115115+bureaucracy is left to the generic functions that interpret the
116116+datatype. One way of understanding the datatype is to devise pickler
117117+combinators for the data model~––~rather than for the values of the
118118+\ml{} language~--~ but leave out the specific value coding machinery
119119+open for interpretation. Another way is to see it as a tagged final
120120+coding of the data model. Concretely the datatype allows to interpose
121121+your own functions at each data model value decoding and encoding
122122+step. These functions can be lossy or creative which naturally leads
123123+to data queries and data updates.
124124+125125+In what follows we focus on presenting the datatype. Providing an
126126+ergonomic set of combinators to construct its values is important but
127127+less difficult. Definitions
128128+are kept as simple as possible to expose the essence of this
129129+\emph{finally tagged} representation. A practical
130130+implementation\footnote{One can be found at
131131+\url{https://erratique.ch/software/jsont}} should enrich these
132132+definitions with documentation strings for data schema documentation
133133+generation, text locations for human friendly error reporting and
134134+text layout information for layout preserving updates.
135135+136136+We use \ocaml{} \citep{ocaml} for the \ml{} language and \json{}
137137+\citep{json} for the data model, but as we conclude in
138138+\autoref{sec:recipe} with the recipe, this technique is independent of
139139+them. For conciseness we use exceptions to represent partial functions
140140+but signatures can be changed to use explicit \code{result} or
141141+\code{either} return types where needed. No effects are needed from
142142+the \ml{} language.
143143+144144+\section{A generic representation}
145145+\label{sec:genrep}
146146+147147+First we define the type \code{Json.t}, a generic representation for
148148+\json{} values in \ml{}. In essence nothing more than an abstract
149149+syntax tree for \json{} text with one case for each sort of value.
150150+151151+\begin{lstlisting}[language=ocaml]
152152+module Json = struct
153153+ type t =
154154+ | Null of unit | Bool of bool | Number of float | String of string
155155+ | Array of t list | Obj of obj and obj = mem list and mem = string * t
156156+end
157157+\end{lstlisting}
158158+159159+As can be seen later, the type \code{Json.t} remains useful. However it is
160160+the type that is unacceptable to work with in \ml{}. Given a fixed
161161+data schema to process we do not want to manipulate this soup of
162162+values:
163163+164164+\begin{itemize}
165165+\item We want objects to be represented by proper record or variant types.
166166+ Not by \code{Json.obj} association lists that must be dynamically name
167167+ checked for expectations.
168168+\item We want to get typed values on array element and object
169169+ member access. Not generic \code{Json.t} values that must be dynamically
170170+ type checked for expectations.
171171+\end{itemize}
172172+173173+\section{A typed representation to interpret}
174174+\label{sec:jsont}
175175+176176+To replace these generic values by the \ml{} values we want, we
177177+introduce the type \code{'a jsont} whose values denote subsets of
178178+\json{} values and their bidirectional map to \ml{} values of type
179179+\code{'a}.
180180+181181+We call these values ``\json{} types''. They belong to the following
182182+generalized algebraic datatype whose cases and elided definitions are
183183+detailed in subsequent sections. The \code{Rec} case is bureaucracy
184184+the laziest readers do not need to care about, it types recursives
185185+\json{} values if your \ml{} is strict.
186186+187187+\begin{lstlisting}[language=ocaml]
188188+type ('a, 'b) base_map = $\ldots$
189189+type ('a, 'elt, 'builder) array_map = $\ldots$
190190+and ('o, 'dec) obj_map = $\ldots$
191191+and 'a any_map = $\ldots$
192192+and ('a, 'b) map = $\ldots$
193193+and _ jsont =
194194+| Null : (unit, 'b) base_map -> 'b jsont
195195+| Bool : (bool, 'b) base_map -> 'b jsont
196196+| Number : (float, 'b) base_map -> 'b jsont
197197+| String : (string, 'b) base_map -> 'b jsont
198198+| Array : ('a, 'elt, 'builder) array_map -> 'a jsont
199199+| Obj : ('o, 'o) obj_map -> 'o jsont
200200+| Any : 'a any_map -> 'a jsont
201201+| Map : ('a, 'b) map -> 'b jsont
202202+| Rec : 'a jsont Lazy.t -> 'a jsont
203203+\end{lstlisting}
204204+%
205205+Except for \code{Any}, \code{Map} and \code{Rec}, the cases of the type
206206+\code{'a jsont} are in direct correspondence with those of
207207+\code{Json.t}. But rather than storing data in the cases we have
208208+functions to bidirectionally map them to values of a type
209209+\code{'a}. The \code{'a jsont} values are used alongside decoding and
210210+encoding processes to directly check and transform the shape of the
211211+data.
212212+213213+For instance we can implement (see \hyperref[sec:appendix]{Appendix}
214214+and \autoref{sec:convert}) these two functions which decode and encode
215215+generic \code{Json.t} values with \ml{} values:
216216+\begin{lstlisting}[language=ocaml]
217217+val decode : 'a jsont -> Json.t -> 'a
218218+val encode : 'a jsont -> 'a -> Json.t
219219+\end{lstlisting}
220220+221221+Representing \json{} data with \ml{} values becomes a matter of
222222+defining suitable \code{'a jsont} values. For example this
223223+kind of \json{} object for messages:
224224+%
225225+\begin{lstlisting}[language=c]
226226+{ "content": "J'aime pas la soupe", "public": true }
227227+\end{lstlisting}
228228+%
229229+can be represented in \ml{} by a record with two fields. Using the
230230+record's natural constructor and field accessors, combinators whose
231231+implementation is given in \autoref{sec:mem_map}, and \ocaml's reverse
232232+function application operator \code{|>}, this kind of object is described by:
233233+234234+\begin{lstlisting}[language=ocaml]
235235+module Message = struct
236236+ type t = { content : string; public : bool }
237237+ let make content public = { content; public }
238238+ let content msg = msg.content
239239+ let public msg = msg.public
240240+ let jsont : t jsont =
241241+ obj_map make
242242+ |> obj_mem "content" string ~enc:content
243243+ |> obj_mem "public" bool ~enc:public
244244+ |> obj_finish
245245+end
246246+\end{lstlisting}
247247+248248+\subsection{Base cases}
249249+\label{sec:base_cases}
250250+251251+Every base case carries a value of type \code{base_map}:
252252+%
253253+\begin{lstlisting}[language=ocaml]
254254+type ('a, 'b) base_map =
255255+{ dec : 'a -> 'b;
256256+ enc : 'b -> 'a; }
257257+\end{lstlisting}
258258+%
259259+Values of this type describe bidirectional maps from values of type
260260+\code{'a} to \code{'b}. They are used to transform the canonical \ml{}
261261+type \code{'a} chosen for a \json{} base type to the one we want to
262262+use. The base cases are as follows:
263263+264264+\begin{itemize}
265265+\item
266266+\code{Null m} maps \json{} nulls to type \code{'a} by mapping
267267+\code{unit} values with \code{m}.
268268+%
269269+\item
270270+\code{Bool m} maps \json{} booleans to type \code{'a} by mapping
271271+\code{bool} values with \code{m}.
272272+%
273273+\item
274274+\code{Number m} maps \json{} numbers or
275275+nulls%%
276276+\footnote{The semantics of \json{} numbers is left to be desired.
277277+Interoperable \json{} implementations map \json{} numbers to
278278+\textsc{ieee} 754 \code{binary64} values. But they are \emph{not} such
279279+values: \textsc{nan} and infinities cannot be represented. As of
280280+writing, the most widely deployed and formally defined \json{}
281281+encoder, namely \textsc{ecmascript}'s \mbox{\code{JSON.stringify}}
282282+\citep{ecmascript}, lossily encodes any non-finite floating point value by
283283+\code{null}.}
284284+%%
285285+to type \code{'a} by mapping \code{float} values with \code{m}.
286286+%
287287+\item
288288+\code{String m} maps unescaped \json{} strings to type \code{'a} by
289289+mapping \code{string} values with \code{m}.
290290+\end{itemize}
291291+292292+Most of the time the map \code{m} used with base cases is the identity
293293+map. But having maps on base types is part of the strategy to
294294+interpose functions in every coding context. This is particulary
295295+useful for \json{} strings which are \json{}'s universal type: all
296296+sorts of enumerations, better represented by variants in \ml{}, can be
297297+found in them. More amusing, to reliably interchange \mbox{64-bit}
298298+integers with \json{} you need to encode them in strings.%%
299299+\footnote{Again, interoperable \json{} implementations map
300300+\json{} numbers to \textsc{ieee} 754 \code{binary64} values. Hence the
301301+only integers that can be interchanged safely without precision loss
302302+are those in the range $[-2^{53};2^{53}]$.}
303303+304304+\subsection{Map case}
305305+\label{sec:map_case}
306306+307307+The elided type \code{map} used by \code{Map} is:
308308+\begin{lstlisting}[language=ocaml]
309309+and ('a, 'b) map =
310310+{ dom : 'a jsont;
311311+ map : ('a, 'b) base_map; }
312312+\end{lstlisting}
313313+%
314314+A \code {Map m} value changes the \ml{} type of the \json{} type
315315+\code{m.dom} from \code{'a} to \code{'b}. It is a tool for composing
316316+\code{jsont} values. If the reader wonders whether it is not simpler
317317+to expose a base case like \code{String m} by the value \code{\{dom =
318318+ String;} \code{ map = m\}}, the answer is rather negative. It is not
319319+directly evident in our simpler exposition but having maps in base
320320+cases provides the proper coding context for erroring or text
321321+layout preserving. This context may be more difficult to recover or no
322322+longer be available to generic functions when they get to process the
323323+\code{Map} case which is not syntactically related to \json{} text.
324324+325325+\pagebreak
326326+327327+\subsection{Array case}
328328+\label{sec:array_case}
329329+330330+The elided type \code{array_map} used by \code{Array} is:
331331+%
332332+\begin{lstlisting}[language=ocaml]
333333+and ('array, 'elt, 'builder) array_map =
334334+{ elt : 'elt jsont;
335335+ dec_empty : 'builder;
336336+ dec_skip : 'builder -> int -> bool;
337337+ dec_add : 'builder -> int -> 'elt -> 'builder;
338338+ dec_finish : 'builder -> 'array;
339339+ enc : 'acc. ('acc -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc; }
340340+\end{lstlisting}
341341+%
342342+An \code{Array m} value maps \json{} arrays of uniform \json{} type
343343+\code{m.elt} to values of type \code{'array} built using values of
344344+type \code{'builder}. The record \code{m} explains how to construct
345345+and deconstruct an \code{'array} value. For decoding, we start with
346346+the value \code{m.dec_empty}, the element in the \json{} array at
347347+index \code{i} is added with \code{m.dec_add}, unless
348348+\code{m.dec_skip} is \code{true} on \code{i} (the purpose of
349349+\code{dec_skip} will become clear later), and the final array is
350350+returned by \code{m.dec_finish}. For encoding, the \code{m.enc}
351351+function folds over the elements of an \code{'array} value to encode
352352+them to the \json{} array.
353353+354354+\subsection{Any case}
355355+\label{sec:any_case}
356356+357357+The elided type \code{any_map} used by \code{Any} is:
358358+%
359359+\begin{lstlisting}[language=ocaml]
360360+and 'a any_map =
361361+{ dec_null : 'a jsont option;
362362+ dec_bool : 'a jsont option;
363363+ dec_number : 'a jsont option;
364364+ dec_string : 'a jsont option;
365365+ dec_array : 'a jsont option;
366366+ dec_obj : 'a jsont option;
367367+ enc : 'a -> 'a jsont; }
368368+\end{lstlisting}
369369+%
370370+An \code{Any m} value maps sets of \json{} values with multiple sorts
371371+to values of type \code{'a}. It embeds dynamic typing in our
372372+datatype. It also allows to decode and encode with different sorts
373373+of \json{} values. For decoding a \json{} value of sort \code{t},
374374+a generic function uses the \json{} type \code{m.dec_t} or errors if
375375+\code{None}. For encoding, the \code{m.enc} function returns the
376376+\json{} type to use with the value.
377377+378378+Given a \json{} type value \code{t} the following \code{option}
379379+combinator uses \code{Any} to make it nullable in \json{}. The result
380380+of \code{option t} is a \json{} type that maps \json{} null values to
381381+\code{None} and otherwise maps \json{} values as \code{t} does but
382382+with successful results wrapped by \code{Some}.
383383+%
384384+\begin{lstlisting}[language=ocaml]
385385+let option : 'a jsont -> 'a option jsont = fun t ->
386386+ let none = Null { dec = Fun.const None; enc = Fun.const () } in
387387+ let some = Map { dom = t; map = {dec = Option.some; enc = Option.get}}in
388388+ let enc = function None -> none | Some _ -> some in
389389+ let none = Some none and some = Some some in
390390+ Any { dec_null = none; dec_bool = some; dec_number = some;
391391+ dec_string = some; dec_array = some; dec_obj = some; enc; }
392392+\end{lstlisting}
393393+394394+The \code{Any} case also allows to devise the \json{} type \code{json}
395395+which maps any \json{} value to its generic representation:
396396+%
397397+\begin{lstlisting}[language=ocaml]
398398+let json : Json.t jsont = $\ldots$
399399+\end{lstlisting}
400400+%
401401+Its definition is left as an exercice for the reader but
402402+this value is a must for partial data schema modelling.
403403+404404+\subsection{Obj case}
405405+\label{sec:obj_case}
406406+407407+Mapping objects is more involved and the design is less
408408+self-evident. Challenges are that members in \json{} objects are
409409+unordered, that the shape of an object may depend on the value of one
410410+if its members\footnote{This is not a built-in mechanism of the data
411411+model but out-of-band constraints mandated by data schemas.} and that
412412+duplicate member names is undefined behaviour.\footnote{But
413413+\textsc{ecmascript}'s formally defined decoder
414414+\mbox{\code{JSON.parse}} \citep{ecmascript} mandates ``last one takes
415415+over.''} This means that we cannot rely on a fixed member ordering to
416416+construct the \ml{} value of an object and worse, that we may have to
417417+wait for its last member to type check it.
418418+419419+To narrow the design space, we focus on a few patterns found in
420420+\json{} data schemas that we want to support without fuss while
421421+retaining efficient decodes for object shapes that are known beforehand.
422422+These patterns are:
423423+%
424424+\begin{enumerate}
425425+\item Objects as records. Member names and their types are
426426+ known beforehand. Members are required or optional in which
427427+ case they can have a default value.
428428+\item Objects as uniform key-value maps. Member names of the object
429429+ are unknown but their values are all of the same type. This
430430+ must compose with pattern 1. as with the \json{} type \code{json}
431431+ (\autoref{sec:any_case})
432432+ it enables partial object modelling
433433+ and supports data schemas that allow foreign members in their
434434+ objects.
435435+\item Objects as sums. There is a distinguished \emph{case member},
436436+ for example named \code{"type"}, \code{"class"} or \code{"version"},
437437+ and its value further determines an object shape described using
438438+ pattern 1., 2. or 3.
439439+\end{enumerate}
440440+%
441441+Finally we want \json{} object maps to be defined through functions that are
442442+already naturally provided for our \ml{} types: constructors and
443443+accessors.
444444+445445+If the shape of an object cannot be captured by these patterns, it is
446446+always possible to map it to a uniform \code{Json.t} key-value map using
447447+pattern 2. followed by a \code{Map} to sort things out. This provides
448448+an ultimate escape hatch at the cost of unconditionnaly going through
449449+the generic representation.
450450+451451+\subsubsection{(De)constructing arbitrary \ml{} values for \json{} objects}
452452+453453+We want to represent \json{} objects by arbitrary \ml{} values of type
454454+\code{'o} which hold member values with their own distinct types
455455+\code{'a}$_1$, \code{'a}$_2$, etc.
456456+457457+For encoding this is easily tackled by having one projection function
458458+of type \code{'o -> 'a}$_i$ for each object member. For decoding we
459459+need to provide a constructor function with one argument per member
460460+value that returns a value of type \code{'o}. To manipulate this
461461+constructor we use a datatype morally equivalent to this
462462+representation of a function application:
463463+\pagebreak
464464+\begin{lstlisting}[language=ocaml]
465465+type ('ret, 'f) app =
466466+| Fun : 'f -> ('ret, 'f) app
467467+| App : ('ret, 'a -> 'b) app * 'a -> ('ret, 'b) app
468468+\end{lstlisting}
469469+%
470470+In a value of type \code{app} we can lift an arbitrary function
471471+\code{f} returning \code{'ret} with the \code{Fun} case and instrument
472472+each argument application with \code{App} cases until \code{f} is
473473+fully ``applied'' to a value of type \code{('ret, 'ret)} \code{app}.
474474+We store object constructors in a similar data type but since we do not
475475+have the argument values yet we use a type witness\footnote{Available
476476+in the \ocaml{} standard library in \code{Type.Id} since \ocaml{} 5.1}
477477+to serve as a placeholder for the member value:
478478+%
479479+\begin{lstlisting}[language=ocaml]
480480+type ('ret, 'f) dec_fun =
481481+| Dec_fun : 'f -> ('ret, 'f) dec_fun
482482+| Dec_app : ('ret, 'a -> 'b) dec_fun * 'a Type.Id.t -> ('ret, 'b) dec_fun
483483+\end{lstlisting}
484484+%
485485+This allows to decode unordered and individually typed member values
486486+as they come, store them by type witness in an heterogeneous
487487+dictionary \code{Dict.t} (see implementation in the
488488+\hyperref[sec:appendix]{Appendix}) and, once we have collected all
489489+member values in the dictionary, we can invoke the constructor to get
490490+the \ml{} value for the object with this function:
491491+%
492492+\begin{lstlisting}[language=ocaml]
493493+let rec apply_dict : type ret f. (ret, f) dec_fun -> Dict.t -> f =
494494+fun dec dict -> match dec with
495495+| Dec_fun f -> f
496496+| Dec_app (f,arg) -> (apply_dict f dict) (Option.get (Dict.find arg dict))
497497+\end{lstlisting}
498498+%
499499+For fully known object shapes this mechanism allows decoders to
500500+directly decode objects and their unordered member values to the
501501+representations we want to use in \ml{}.
502502+503503+\subsubsection{Object maps}
504504+\label{sec:obj_map}
505505+506506+The elided type \code{obj_map} used by \code{Obj} is:
507507+508508+\begin{lstlisting}[language=ocaml]
509509+and ('o, 'dec) obj_map =
510510+{ dec : ('o, 'dec) dec_fun;
511511+ mem_decs : mem_dec String_map.t;
512512+ mem_encs : 'o mem_enc list;
513513+ shape : 'o obj_shape; }
514514+\end{lstlisting}
515515+%
516516+An \code{Obj m} value maps a \json{} object to a value of type
517517+\code{'o}. The \code{m.dec} field holds the constructor function for
518518+\code{'o} values. The \code{Obj} case in the definition of
519519+\code{jsont} (\autoref{sec:jsont}) constrains the \code{'dec}
520520+parameter to be equal to \code{'o} which ensures that the contructor
521521+is fully ``applied''. Remaining fields of the record are described in
522522+subsequent sections.
523523+524524+\subsubsection{Member maps}
525525+\label{sec:mem_map}
526526+527527+The \code{m.mem_decs} and \code{m.mem_encs} fields of \code{obj_map}
528528+describe members of the object that are known beforehand. Both fields
529529+hold the same values of type \code{mem_map} but they are sorted
530530+differently and their type parameters are hidden in slighly different
531531+ways to accomodate decoding and encoding processes. These types are
532532+defined by:
533533+\pagebreak
534534+\begin{lstlisting}[language=ocaml]
535535+and mem_dec = Mem_dec : ('o, 'a) mem_map -> mem_dec
536536+and 'o mem_enc = Mem_enc : ('o, 'a) mem_map -> 'o mem_enc
537537+and ('o, 'a) mem_map =
538538+{ name : string;
539539+ type' : 'a jsont;
540540+ id : 'a Type.Id.t;
541541+ dec_absent : 'a option;
542542+ enc : 'o -> 'a;
543543+ enc_omit : 'a -> bool; }
544544+\end{lstlisting}
545545+%
546546+A value \code{mm} of type \code{mem_map} maps a member \code{'a} of a
547547+\json{} object mapped to \code{'o}. \code{mm.name} is the member
548548+name. \code{mm.type'} is the \json{} type of its value. \code{mm.id}
549549+is the type witness to represent the member value in the constructor
550550+of \code{'o}. \code{mm.dec_absent} is a value to use if the member is
551551+absent on decodes; \code{None} means error on absence. \code{mm.enc}
552552+is the function to get back the member value from \code{'o} for
553553+encoding. \code{mm.enc_omit} is a predicate on the value returned by
554554+\code{mm.enc} to decide whether it should be omitted on encoding;
555555+usually this tests for equality with the value mentioned in
556556+\code{mm.dec_absent}.
557557+558558+A member map \code{mm} needs to be added to an object map \code{m} in
559559+\code{m.mem_decs}, \code{m.mem_encs} and the \code{mm.id} type witness
560560+must be applied to the object constructor in \code{m.dec}. This is the
561561+duty of combinators. For example this one describes a required member
562562+and adds it to an object map:
563563+564564+\begin{lstlisting}[language=ocaml]
565565+let obj_mem :
566566+ string -> 'a jsont -> enc:('o -> 'a) ->
567567+ ('o, 'a -> 'b) obj_map -> ('o, 'b) obj_map
568568+=
569569+fun name type' ~enc obj_map ->
570570+ let id = Type.Id.make () in
571571+ let dec_absent = None and enc_omit = Fun.const false in
572572+ let mm = { name; type'; id; dec_absent; enc; enc_omit } in
573573+ let dec = Dec_app (obj_map.dec, mm.id) in
574574+ let mem_decs = String_map.add mm.name (Mem_dec mm) obj_map.mem_decs in
575575+ let mem_encs = Mem_enc mm :: obj_map.mem_encs in
576576+ { obj_map with dec; mem_decs; mem_encs; }
577577+\end{lstlisting}
578578+%
579579+At this point we can provide the full implementations of the combinators
580580+used in the message object modelling example given in \autoref{sec:jsont}.
581581+%
582582+\begin{lstlisting}[language=ocaml]
583583+let bool = Bool { dec = Fun.id; enc = Fun.id }
584584+let string = String { dec = Fun.id; enc = Fun.id }
585585+let obj_finish o = Obj { o with mem_encs = List.rev o.mem_encs }
586586+let obj_map : 'dec -> ('o, 'dec) obj_map = fun make ->
587587+ let dec = Dec_fun make and shape = Obj_basic Unknown_skip in
588588+ { dec; mem_decs = String_map.empty; mem_encs = []; shape }
589589+\end{lstlisting}
590590+591591+\pagebreak
592592+\subsubsection{Object shapes}
593593+\label{sec:obj_shape}
594594+595595+The last field of the \code{obj_map} type to describe is the
596596+\code{shape} field of type \code{obj_shape}:
597597+\begin{lstlisting}[language=ocaml]
598598+and 'o obj_shape =
599599+| Obj_basic : ('o, 'mems, 'builder) unknown_mems -> 'o obj_shape
600600+| Obj_cases : ('o, 'cases, 'tag) obj_cases -> 'o obj_shape
601601+\end{lstlisting}
602602+%
603603+This value indicates whether the members described in the object map
604604+are the final word on the shape of the object:
605605+606606+\begin{itemize}
607607+\item \code{Obj_basic u} indicates that the object's members are fully
608608+known and the way to handle unknown member is described by \code{u}, see \autoref{sec:unknown_mems}.
609609+\item \code{Obj_cases cases} indicates that there is a case
610610+ member described in \code{cases}. Each case member value
611611+ gives another \code{obj_map} value which further describe the object, see
612612+ \autoref{sec:obj_cases}.
613613+\end{itemize}
614614+%
615615+The \code{obj_shape} type definition turns object map values into a
616616+decision tree with \code{Obj_cases} nodes, branches labelled by case
617617+member values and with \code{Obj_basic} leaves. Each path in this tree
618618+describes a complete object whose members depend on case member
619619+values found in the data. We assume that the combinators constructing
620620+these values enforce the constraint that no member is defined twice in
621621+a path from the root to a leaf.
622622+623623+Note that once you get an \code{Obj_basic} shape, all data dependent
624624+shapes have been determined and members can be directly decoded to
625625+their type without buffering them.
626626+627627+628628+\subsubsection{Unknown members}
629629+\label{sec:unknown_mems}
630630+631631+The type \code{unknown_mems} used by \code{Obj_basic} shapes is:
632632+633633+\begin{lstlisting}[language=ocaml]
634634+and ('o, 'mems, 'builder) unknown_mems =
635635+| Unknown_skip : ('o, unit, unit) unknown_mems
636636+| Unknown_error : ('o, unit, unit) unknown_mems
637637+| Unknown_keep :
638638+ ('mems, 'a, 'builder) mems_map * ('o -> 'mems) ->
639639+ ('o, 'mems) unknown_mems
640640+\end{lstlisting}
641641+%
642642+A value \code{u} of type \code{unknown_mems} maps to \code{'mems} the
643643+unknown members of a \json{} object mapped to \code{'o}. It
644644+respectively indicates to skip, error, or keep them. In the latter
645645+case the \code{Unknown_keep (m, enc)} value describes with \code{enc} how
646646+to get them back from \code{'o} for encoding and with \code{m}, how to map
647647+them to a value of type \code{'mems}. The values \code{enc} and
648648+\code{m} are kept separate because the type \code{'o} is bespoke while
649649+unknown member maps can be reused across object maps. The value
650650+\code{m} is of this type:
651651+%
652652+\begin{lstlisting}[language=ocaml]
653653+and ('mems, 'a, 'builder) mems_map =
654654+{ mems_type : 'a jsont;
655655+ id : 'mems Type.Id.t;
656656+ dec_empty : 'builder;
657657+ dec_add : string -> 'a -> 'builder -> 'builder;
658658+ dec_finish : 'builder -> 'mems
659659+ enc : 'acc. (string -> 'a -> 'acc -> 'acc) -> 'mems -> 'acc -> 'acc }
660660+\end{lstlisting}
661661+%
662662+This record maps unknown members of uniform \json{} type
663663+\code{m.mems_type} to a value of type \code{'mems} built using values
664664+of types \code{'builder}. Use the \json{} type \code{json}
665665+(\autoref{sec:any_case}) in \code{m.mems_type} for partial object
666666+modelling or objects that need to preserve foreign
667667+members. \code{m.id} is the type witness to represent the \code{'mems}
668668+value in the object constructor. For decoding, we start with the value
669669+\code{m.dec_empty}, unknown members are added with \code{m.dec_add}
670670+and the final \code{'mems} value is returned by
671671+\code{m.dec_finish}. For encoding \code{m.enc} allows to recover from
672672+\code{'mems} the unknown members to encode them in the \json{} object.
673673+674674+675675+\subsubsection{Object cases}
676676+\label{sec:obj_cases}
677677+678678+Type type \code{obj_cases} used by \code{Obj_cases} shapes is:
679679+680680+\begin{lstlisting}[language=ocaml]
681681+and ('o, 'cases, 'tag) obj_cases =
682682+{ tag : ('o, 'tag) mem_map; (* 'o is irrelevant, 'tag is not stored *)
683683+ tag_compare : 'tag -> 'tag -> int;
684684+ id : 'cases Type.Id.t;
685685+ cases : ('cases, 'tag) case list;
686686+ enc : 'o -> 'cases;
687687+ enc_case : 'cases -> ('cases, 'tag) case_value; }
688688+\end{lstlisting}
689689+%
690690+A value \code{m} of type \code{obj_cases} maps to \code{'cases} the
691691+object cases of an object mapped to \code{'o}. Cases are selected by
692692+the value of a case member of type \code{'tag} described in
693693+\code{m.tag}. Tag values are not stored in \code{'o} (the decoded case
694694+value is) so the \code{'o} parameter, \code{m.tag.id} and
695695+\code{m.tag.enc} are unused here. \code{m.tag_compare} allows to
696696+compare case tags. \code{m.id} is the type witness to represent the
697697+cases in the constructor of \code{'o}. \code{m.cases} is the list of
698698+cases. This is not a function on \code{'tag} values in order to make
699699+the description enumerable (e.g. for schema documentation
700700+generation). The type \code{case} hides the \code{'case}
701701+parameter of the type \code{case_map} which describes cases:
702702+703703+\begin{lstlisting}[language=ocaml]
704704+and ('cases, 'tag) case =
705705+| Case : ('cases, 'case, 'tag) case_map -> ('cases, 'tag) case
706706+707707+and ('cases, 'case, 'tag) case_map =
708708+{ tag : 'tag;
709709+ obj_map : ('case, 'case) obj_map;
710710+ dec : 'case -> 'cases; }
711711+\end{lstlisting}
712712+%
713713+A value \code{cm} of type \code{case_map} describes a case of type
714714+\code{'case} part of the type \code{'cases}. \code{cm.tag} is the tag
715715+value that identifies the case in the data. \code{cm.obj_map} describes the
716716+additional shape this case gives to the object. \code{cm.dec} injects
717717+the decoded case into the type that gathers them.
718718+719719+For encoding cases, the \code{m.enc} function of \code{obj_cases}
720720+gets back the case from \code{'o}. To find out how to encode it, the
721721+function \code{m.enc_case} is used. It returns a value of type
722722+\code{case_value} which has a the actual case value and its map
723723+for encoding:
724724+725725+\begin{lstlisting}[language=ocaml]
726726+and ('cases, 'tag) case_value =
727727+| Case_value :
728728+ ('cases, 'case, 'tag) case_map * 'case -> ('cases, 'tag) case_value
729729+\end{lstlisting}
730730+%
731731+The \code{m.enc_case} function is the only ad-hoc function that needs
732732+to be devised specifically for \code{jsont} values. All the other
733733+functions to describe objects are natural constructors and accessors
734734+of \ml{} types.
735735+736736+The design for object cases allows to map them to a record type
737737+which has common fields for all cases and a field for the cases:
738738+%
739739+\begin{lstlisting}[language=ocaml]
740740+type type' = C1 of C1.t | C2 of C2.t $\ldots$
741741+type t =
742742+{ $\ldots$ (* Fields common to all cases *); type' : type'; }
743743+\end{lstlisting}
744744+%
745745+but they can also be described individually and mapped to
746746+ a ``toplevel'' variant type if \code{'cases} coincides with \code{'o}:
747747+%
748748+\begin{lstlisting}[language=ocaml]
749749+type t = C1 of C1.t | C2 of C2.t $\ldots$
750750+\end{lstlisting}
751751+752752+\section{Decode and encode}
753753+\label{sec:convert}
754754+755755+Given a \code{jsont} value we can decode and encode \json{} with \ml{}
756756+values without constructing generic \code{Json.t} values; except
757757+transiently for decoding object instances with data dependent shapes
758758+and poorly ordered members. Implementing a \json{} codec is beyond the
759759+scope of this paper but the \hyperref[sec:appendix]{Appendix} has
760760+implementations for \code{decode} and \code{encode} functions that convert
761761+\ml{} values with generic \code{Json.t} values.
762762+763763+For \code{decode} we took care not to assume full in-memory access to
764764+an object's members. It thus shows how a decoder can proceed to
765765+provide best-effort on-the-fly decoding. Except for case members, the
766766+last occurence of duplicate members takes over, however all definitions
767767+must type as defined by the object map otherwise the decode
768768+errors. These limitations on duplicate members could be lifted with a
769769+more complex decoder but it may not be worth the trouble. The case for
770770+objects is more intricate than we would like it to be, but we blame
771771+\json{}'s loose specification for that.
772772+773773+Otherwise the implementation of these functions mostly consists in
774774+recursing on the \code{jsont} values to boringly invoke the menagerie
775775+of functions that are packed therein.
776776+777777+\section{Query and update}
778778+\label{sec:queryx}
779779+780780+Since we can now interpose our functions in every coding context we
781781+get a very flexible data processing system. A type for data queries
782782+and a function to execute them can be as simple as:
783783+784784+\begin{lstlisting}[language=ocaml]
785785+type 'a query = 'a jsont
786786+let query : 'a query -> Json.t -> 'a = decode
787787+\end{lstlisting}
788788+%
789789+In this view, queries are just transforming decodes. Their encoding
790790+direction can be made to fail or defined with anything that feels
791791+sensitive to encode the query result to.
792792+793793+To navigate the structure of \json{} values to apply a query on a
794794+subtree, the following composable indexing combinators can be used:
795795+796796+\begin{lstlisting}[language=ocaml]
797797+let get_mem : string -> 'a query -> 'a query = fun name q ->
798798+ obj_map Fun.id |> obj_mem name q ~enc:Fun.id |> obj_finish
799799+800800+let get_nth : int -> 'a query -> 'a query = fun nth q ->
801801+ let dec_empty = None and dec_add _ _ v = Some v in
802802+ let dec_skip _ k = nth <> k in
803803+ let dec_finish = function None -> failwith "too short" | Some v -> v in
804804+ let enc f acc v = f acc v (* Singleton array with the query result *) in
805805+ Array { elt = q; dec_empty; dec_add; dec_skip; dec_finish; enc }
806806+\end{lstlisting}
807807+%
808808+The \code{get_nth} combinator explains the presence of \code{dec_skip}
809809+in the \code{array_map} type (\autoref{sec:array_case}). The query
810810+\code{q} only needs to succeed on the \code{nth} element. Without
811811+\code{dec_skip} we would apply it on every element of the array which
812812+is undesirable. The \code{dec_skip} field is the only bit in the
813813+design that was specifically added to support queries. For objects,
814814+skipping unknown members is quite natural to have in order to support data
815815+schema evolution.
816816+817817+Typed updates of \json{} data is easy to specify as \code{Json.t}
818818+returning \json{} types. Decoders invoking such queries return updated
819819+\json{} as \code{Json.t} values. Here is a kernel of composable
820820+combinators to peform updates:
821821+822822+\begin{lstlisting}[language=ocaml]
823823+val update_mem : string -> 'a jsont -> Json.t jsont
824824+val update_nth : int -> 'a jsont -> Json.t jsont
825825+val delete_mem : string -> Json.t jsont
826826+val delete_nth : int -> Json.t jsont
827827+val const : 'a jsont -> 'a -> 'a jsont
828828+\end{lstlisting}
829829+%
830830+The \code{update_mem} and \code{update_nth} combinators apply
831831+on the member or index value the decoder of the given \json{} type
832832+and replace it with the encoding of the result. Chaining update
833833+combinators allows to navigate arbitrarily nested \json{} to apply an
834834+update. All these combinators are simple \code{Map} over the \json{}
835835+type \code{json} (\autoref{sec:any_case}) with suitable uses of
836836+\code{encode} and \code{decode}. The implementations of
837837+\code{update_mem}, \code{delete_mem} and \code{const} are:
838838+839839+\begin{lstlisting}[language=ocaml]
840840+let update_mem : string -> 'a jsont -> Json.t jsont = fun name q ->
841841+ let dec = function
842842+ | Json.Obj ms ->
843843+ let update (n, v as m) =
844844+ if n = name then (n, encode q (decode q v)) else m
845845+ in
846846+ Json.Obj (List.map update ms)
847847+ | _ -> failwith "type error"
848848+ in
849849+ Map { dom = json; map = { dec; enc = Fun.id } }
850850+851851+let delete_mem : string -> Json.t jsont = fun name ->
852852+ let dec = function
853853+ | Json.Obj ms -> Json.Obj (List.filter (fun (n, _) -> n <> name) ms)
854854+ | _ -> type_error ()
855855+ in
856856+ Map { dom = json; map = { dec; enc = Fun.id } }
857857+858858+let const : 'a jsont -> 'a -> 'a jsont = fun t v ->
859859+ let dec _ = v and enc _ = encode t v in
860860+ Map { dom = json; map = { dec; enc } }
861861+\end{lstlisting}
862862+\section{The recipe}
863863+\label{sec:recipe}
864864+865865+None of what was presented here is specific to the \json{} data
866866+model. A datatype similar to \code{jsont} (\autoref{sec:jsont}) can be
867867+devised for any data model. The recipe is as follows.
868868+%
869869+\begin{itemize}
870870+\item A base case is needed for every base type of the model. Having
871871+ maps in these cases allows to accurately represent their coding contexts.
872872+ (\autoref{sec:base_cases})
873873+\item An \code{Array}-like case is needed for mapping the model's type for
874874+ arrays. (\autoref{sec:array_case})
875875+\item An \code{Obj}-like case is needed for mapping the model's type for
876876+ key-value maps or records. The \ml{} ingredients here are:
877877+ projection functions for encoding and, for decoding, a constructor
878878+ function instrumented by a datatype representing function applications
879879+ using type witnesses to indirectly refer to argument values.
880880+ (\autoref{sec:obj_case})
881881+\item An \code{Any}-like case is needed if the model is dynamically
882882+ typed. It is used to map implicit sums of the model's types to
883883+ a uniform \ml{} type. (\autoref{sec:any_case})
884884+\item The \code{Map} case is needed for composing map values.
885885+ (\autoref{sec:map_case})
886886+\item The \code{Rec} case is needed in a strict \ml{} for representing
887887+ recursive values of the data model. (\autoref{sec:jsont})
888888+\end{itemize}
889889+%
890890+And with this we hope to have made your future data soups more edible
891891+in \ml{}.
892892+893893+\appendix
894894+895895+\begin{thebibliography}{}
896896+\bibitem[\protect\citename{Bray, }2017]{json}
897897+ Bray, T., Ed. (2017)
898898+ The JavaScript Object Notation (JSON) Data Interchange Format.
899899+ {\it RFC 8259}.
900900+ \url{https://doi.org/10.17487/RFC8259}
901901+902902+\bibitem[\protect\citename{Guo, }2023]{ecmascript}
903903+ Guo, S., Ficarra M., Gibbons, K., Eds (2023)
904904+ ECMAScript® 2023 Language Specification. ECMA-262.
905905+ \url{https://262.ecma-international.org/14.0/}
906906+\bibitem[\protect\citename{Gibbons, }2007]{datatypegeneric}
907907+ Gibbons, J. (2007)
908908+ Datatype-Generic Programming. {\it Lecture Notes in Computer Science}.
909909+ vol 4719.
910910+ \url{https://doi.org/10.1007/978-3-540-76786-2\_1}
911911+912912+\bibitem[\protect\citename{Kennedy, }2004]{picklers}
913913+ Kennedy, A. J., (2004)
914914+ Pickler combinators. {\it Journal of Functional Programming}.
915915+ 14(6), 727-739.
916916+ \url{https://doi.org/10.1017/S0956796804005209}
917917+918918+\bibitem[\protect\citename{Leroy {\it et al.}, }2023]{ocaml}
919919+ Leroy, X., Doligez, D., Frisch, A., Garrigue, J., Rémy, D.,
920920+ Sivaramakrishnan, KC, \& Vouillon, J. (2023)
921921+ The OCaml system release 5.1. Documentation and user’s manual.
922922+ \mbox{\url{https://ocaml.org/manual}}
923923+\end{thebibliography}
924924+925925+926926+\section*{Appendix}
927927+\label{sec:appendix}
928928+929929+\ocaml{} 5.1 implementation of the \code{decode} and \code{encode}
930930+functions mentioned in \autoref{sec:convert}.
931931+932932+\lstset{basicstyle=\relscale{0.84}\ttfamily\linespread{1.1}\selectfont}
933933+\begin{lstlisting}[language=ocaml]
934934+935935+module String_map = Map.Make (String)
936936+937937+(* Errors *)
938938+939939+let type_error () = failwith "type error"
940940+let unexpected_member n = failwith ("Unexpected member " ^ n)
941941+let missing_member n = failwith ("Missing member " ^ n)
942942+let unknown_case_tag () = failwith "Unknown case tag"
943943+944944+945945+946946+(* Heterogeneous key-value maps *)
947947+948948+module Dict = struct
949949+ module M = Map.Make (Int)
950950+ type binding = B : 'a Type.Id.t * 'a -> binding
951951+ type t = binding M.t
952952+ let empty = M.empty
953953+ let add k v m = M.add (Type.Id.uid k) (B (k, v)) m
954954+ let find : type a. a Type.Id.t -> t -> a option =
955955+ fun k m -> match M.find_opt (Type.Id.uid k) m with
956956+ | None -> None
957957+ | Some B (k', v) ->
958958+ match Type.Id.provably_equal k k' with
959959+ | Some Type.Equal -> Some v | None -> assert false
960960+end
961961+962962+(* Decode *)
963963+964964+let rec decode : type a. a jsont -> Json.t -> a =
965965+fun t j -> match t with
966966+| Null map -> (match j with Json.Null v -> map.dec v | _ -> type_error ())
967967+| Bool map -> (match j with Json.Bool b -> map.dec b | _ -> type_error ())
968968+| Number map ->
969969+ (match j with
970970+ | Json.Number n -> map.dec n | Json.Null _ -> map.dec Float.nan
971971+ | _ -> type_error ())
972972+| String map -> (match j with Json.String s -> map.dec s | _ -> type_error ())
973973+| Array map ->
974974+ (match j with Json.Array vs -> decode_array map vs | j -> type_error ())
975975+| Obj map ->
976976+ (match j with Json.Obj mems -> decode_obj map mems | j -> type_error ())
977977+| Map map -> map.map.dec (decode map.dom j)
978978+| Any map -> decode_any t map j
979979+| Rec t -> decode (Lazy.force t) j
980980+981981+and decode_array : type a e b. (a, e, b) array_map -> Json.t list -> a =
982982+fun map vs ->
983983+ let add (i, a) v =
984984+ i + 1, (if map.dec_skip a i then a else map.dec_add a i (decode map.elt v))
985985+ in
986986+ map.dec_finish (snd (List.fold_left add (0, map.dec_empty) vs))
987987+988988+and decode_obj : type o. (o, o) obj_map -> Json.obj -> o =
989989+fun map mems ->
990990+ apply_dict map.dec @@
991991+ decode_obj_map map String_map.empty String_map.empty Dict.empty mems
992992+993993+and decode_obj_map : type o.
994994+ (o, o) obj_map -> mem_dec String_map.t -> mem_dec String_map.t -> Dict.t ->
995995+ Json.obj -> Dict.t
996996+=
997997+fun map mem_miss mem_decs dict mems ->
998998+ let u n _ _ = invalid_arg (n ^ "member defined twice") in
999999+ let mem_miss = String_map.union u mem_miss map.mem_decs in
10001000+ let mem_decs = String_map.union u mem_decs map.mem_decs in
10011001+ match map.shape with
10021002+ | Obj_cases cases -> decode_obj_case cases mem_miss mem_decs dict [] mems
10031003+ | Obj_basic u ->
10041004+ match u with
10051005+ | Unknown_skip -> decode_obj_basic u () mem_miss mem_decs dict mems
10061006+ | Unknown_error -> decode_obj_basic u () mem_miss mem_decs dict mems
10071007+ | Unknown_keep (map, _) ->
10081008+ decode_obj_basic u map.dec_empty mem_miss mem_decs dict mems
10091009+10101010+and decode_obj_basic : type o map builder.
10111011+ (o, map, builder) unknown_mems -> builder -> mem_dec String_map.t ->
10121012+ mem_dec String_map.t -> Dict.t -> Json.obj -> Dict.t
10131013+=
10141014+fun u umap mem_miss mem_decs dict -> function
10151015+| [] ->
10161016+ let dict = match u with
10171017+ | Unknown_skip | Unknown_error -> dict
10181018+ | Unknown_keep (map, _) -> Dict.add map.id (map.dec_finish umap) dict
10191019+ in
10201020+ let add_default _ (Mem_dec m) dict = match m.dec_absent with
10211021+ | Some v -> Dict.add m.id v dict | None -> missing_member m.name
10221022+ in
10231023+ String_map.fold add_default mem_miss dict
10241024+| (n, v) :: mems ->
10251025+ match String_map.find_opt n mem_decs with
10261026+ | Some (Mem_dec m) ->
10271027+ let dict = Dict.add m.id (decode m.type' v) dict in
10281028+ let mem_miss = String_map.remove n mem_miss in
10291029+ decode_obj_basic u umap mem_miss mem_decs dict mems
10301030+ | None ->
10311031+ match u with
10321032+ | Unknown_skip -> decode_obj_basic u umap mem_miss mem_decs dict mems
10331033+ | Unknown_error -> unexpected_member n
10341034+ | Unknown_keep (map, _) ->
10351035+ let umap = map.dec_add n (decode map.mems_type v) umap in
10361036+ decode_obj_basic u umap mem_miss mem_decs dict mems
10371037+10381038+and decode_obj_case : type o cases tag.
10391039+ (o, cases, tag) obj_cases -> mem_dec String_map.t -> mem_dec String_map.t ->
10401040+ Dict.t -> Json.obj -> Json.obj -> Dict.t
10411041+=
10421042+fun cases mem_miss mem_decs dict delay mems ->
10431043+ let decode_case_tag tag =
10441044+ let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
10451045+ match List.find_opt eq_tag cases.cases with
10461046+ | None -> unknown_case_tag ()
10471047+ | Some (Case case) ->
10481048+ let mems = List.rev_append delay mems in
10491049+ let dict = decode_obj_map case.obj_map mem_miss mem_decs dict mems in
10501050+ Dict.add cases.id (case.dec (apply_dict case.obj_map.dec dict)) dict
10511051+ in
10521052+ match mems with
10531053+ | [] ->
10541054+ (match cases.tag.dec_absent with
10551055+ | Some t -> decode_case_tag t | None -> missing_member cases.tag.name)
10561056+ | (n, v as mem) :: mems ->
10571057+ if n = cases.tag.name then decode_case_tag (decode cases.tag.type' v) else
10581058+ match String_map.find_opt n mem_decs with
10591059+ | None -> decode_obj_case cases mem_miss mem_decs dict (mem :: delay) mems
10601060+ | Some (Mem_dec m) ->
10611061+ let dict = Dict.add m.id (decode m.type' v) dict in
10621062+ let mem_miss = String_map.remove n mem_miss in
10631063+ decode_obj_case cases mem_miss mem_decs dict delay mems
10641064+10651065+10661066+and decode_any : type a. a jsont -> a any_map -> Json.t -> a =
10671067+fun t map j ->
10681068+ let dec t m j = match m with Some t -> decode t j | None -> type_error () in
10691069+ match j with
10701070+ | Json.Null _ -> dec t map.dec_null j
10711071+ | Json.Bool _ -> dec t map.dec_bool j
10721072+ | Json.Number _ -> dec t map.dec_number j
10731073+ | Json.String _ -> dec t map.dec_string j
10741074+ | Json.Array _ -> dec t map.dec_array j
10751075+ | Json.Obj _ -> dec t map.dec_obj j
10761076+10771077+(* Encode *)
10781078+10791079+let rec encode : type a. a jsont -> a -> Json.t =
10801080+fun t v -> match t with
10811081+| Null map -> Json.Null (map.enc v)
10821082+| Bool map -> Json.Bool (map.enc v)
10831083+| Number map -> Json.Number (map.enc v)
10841084+| String map -> Json.String (map.enc v)
10851085+| Array map ->
10861086+ let encode_elt a elt = (encode map.elt elt) :: a in
10871087+ Json.Array (List.rev (map.enc encode_elt [] v))
10881088+| Obj map -> Json.Obj (List.rev (encode_obj map v []))
10891089+| Any map -> encode (map.enc v) v
10901090+| Map map -> encode map.dom (map.map.enc v)
10911091+| Rec t -> encode (Lazy.force t) v
10921092+10931093+and encode_obj : type o. (o, o) obj_map -> o -> Json.obj -> Json.obj =
10941094+fun map o obj ->
10951095+ let encode_mem obj (Mem_enc map) =
10961096+ let v = map.enc o in
10971097+ if map.enc_omit v then obj else (map.name, encode map.type' v) :: obj
10981098+ in
10991099+ let obj = List.fold_left encode_mem obj map.mem_encs in
11001100+ match map.shape with
11011101+ | Obj_basic (Unknown_keep (map, enc)) ->
11021102+ let encode_mem n v obj = (n, encode map.mems_type v) :: obj in
11031103+ map.enc encode_mem (enc o) obj
11041104+ | Obj_basic _ -> obj
11051105+ | Obj_cases cases ->
11061106+ let Case_value (case, c) = cases.enc_case (cases.enc o) in
11071107+ let obj =
11081108+ if cases.tag.enc_omit case.tag then obj else
11091109+ (cases.tag.name, encode cases.tag.type' case.tag) :: obj
11101110+ in
11111111+ encode_obj case.obj_map c obj
11121112+\end{lstlisting}
11131113+11141114+\label{lastpage01}
11151115+\end{document}
+142
vendor/opam/jsont/paper/soup_test.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 Daniel C. Bünzli. All rights reserved.
33+ SPDX-License-Identifier: CC0-1.0
44+ ---------------------------------------------------------------------------*)
55+66+(* Tests for soup.ml *)
77+88+open B0_testing
99+1010+open Soup
1111+1212+(* More combinators *)
1313+1414+let number = Number { dec = Fun.id; enc = Fun.id }
1515+let array elt =
1616+ let dec_empty = [] and dec_add a _i v = v :: a in
1717+ let dec_finish elts = List.rev elts in
1818+ let dec_skip _ _ = false in
1919+ let enc f acc vs = List.fold_left f acc vs in
2020+ Array { elt; dec_empty; dec_add; dec_skip; dec_finish; enc }
2121+2222+(* Test data *)
2323+2424+let content = "J'aime pas la soupe" and public = true
2525+let json_msg0 = Json.(Obj ["public", Bool public; "content", String content])
2626+let json_msg1 =
2727+ Json.(Obj ["content", String "Heyho!"; "public", Bool public; "time",
2828+ Number 1.])
2929+3030+let json_msg3 =
3131+ Json.(Obj ["public", Bool public; "content", String (content ^ "!")])
3232+3333+let json_msgs = Json.Array [json_msg0; json_msg1]
3434+3535+(* Tests *)
3636+3737+let test_trip () =
3838+ Test.test "generic trip test" @@ fun () ->
3939+ let dec = decode json json_msgs in
4040+ let trip = encode json dec in
4141+ if json_msgs <> trip
4242+ then (Test.log_fail "json_msgs <> trip"; assert false);
4343+ ()
4444+4545+let test_msg () =
4646+ Test.test "Message modelling and queries tests" @@ fun () ->
4747+ let msg = { Message.content; public } in
4848+ let msg' = decode Message.jsont json_msg0 in
4949+ if msg <> msg' then (Test.log_fail "msg <> msg'"; assert false);
5050+ let q n = get_nth n @@ get_mem "time" number in
5151+ assert (query (q 1) json_msgs = 1.);
5252+ Test.failure @@ (fun () -> query (q 0) json_msgs);
5353+ let json_msgs' =
5454+ let q =
5555+ update_nth 0 @@ update_mem "content" @@
5656+ map (fun s -> s ^ "!") Fun.id string
5757+ in
5858+ query (delete_nth 1) (query q json_msgs)
5959+ in
6060+ if json_msgs' <> Json.Array[json_msg3]
6161+ then (Test.log_fail "json_msgs''"; assert false);
6262+ let json_msgs' =
6363+ let q =
6464+ update_nth 0 @@ update_mem "content" (const string (content ^ "!"))
6565+ in
6666+ (query q json_msgs)
6767+ in
6868+ if json_msgs' <> Json.Array[json_msg3;json_msg1]
6969+ then (Test.log_fail "json_msgs''"; assert false);
7070+ ()
7171+7272+module Cases = struct
7373+7474+ type point = { x : float; y : float }
7575+ type line = { p0 : point; p1 : point }
7676+ type type' = Point of point | Line of line
7777+ type geom = { name : string; type' : type' }
7878+7979+ (* more data *)
8080+8181+ let ml_geom =
8282+ { name = "Hey";
8383+ type' = Line { p0 = { x = 0.; y = 1.}; p1 = { x = 2.; y = 3.}} }
8484+8585+ let json_geom = (* out of order *)
8686+ Json.(Obj ["name", String "Hey";
8787+ "p0", Obj ["x", Number 0.; "y", Number 1.];
8888+ "p1", Obj ["y", Number 3.; "x", Number 2.];
8989+ "type", String "line"; ])
9090+9191+ (* JSON types *)
9292+9393+ let point_jsont =
9494+ obj_map (fun x y -> { x; y })
9595+ |> obj_mem "x" number ~enc:(fun p -> p.x)
9696+ |> obj_mem "y" number ~enc:(fun p -> p.y)
9797+9898+ let line_jsont =
9999+ let point = obj_finish point_jsont in
100100+ obj_map (fun p0 p1 -> { p0; p1 })
101101+ |> obj_mem "p0" point ~enc:(fun p -> p.p0)
102102+ |> obj_mem "p1" point ~enc:(fun p -> p.p1)
103103+104104+ let case_point =
105105+ { tag = "point"; obj_map = point_jsont; dec = fun p -> Point p }
106106+107107+ let case_line =
108108+ { tag = "line"; obj_map = line_jsont; dec = fun l -> Line l }
109109+110110+ let cases =
111111+ { tag = { name = "type"; type' = string; id = Type.Id.make ();
112112+ dec_absent = None; enc = (fun _ -> assert false);
113113+ enc_omit = (fun _ -> assert false); };
114114+ tag_compare = String.compare;
115115+ id = Type.Id.make ();
116116+ cases = [Case case_point; Case case_line];
117117+ enc = (fun g -> g.type');
118118+ enc_case = (function
119119+ | Point p -> Case_value (case_point, p)
120120+ | Line l -> Case_value (case_line, l)) }
121121+122122+ let geom_jsont : geom jsont =
123123+ let obj = obj_map (fun name type' -> { name; type' }) in
124124+ let obj = obj_mem "name" string obj ~enc:(fun g -> g.name) in
125125+ obj_finish @@
126126+ { obj with shape = Obj_cases cases; dec = Dec_app (obj.dec, cases.id) }
127127+end
128128+129129+let test_cases () =
130130+ Test.test "cases" @@ fun () ->
131131+ let g = decode Cases.geom_jsont Cases.json_geom in
132132+ if Cases.ml_geom <> g then (Test.log_fail "Cases.geom.ml <> g"; assert false);
133133+ ()
134134+135135+let main () =
136136+ Test.main @@ fun () ->
137137+ test_trip ();
138138+ test_msg ();
139139+ test_cases ();
140140+ ()
141141+142142+let () = if !Sys.interactive then () else exit (main ())
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Jsont.Repr
77+88+(* Converting between Jsont.Error.t and Jv.Error.t values *)
99+1010+let error_to_jv_error e = Jv.Error.v (Jstr.of_string (Jsont.Error.to_string e))
1111+let jv_error_to_error e =
1212+ let ctx = Jsont.Error.Context.empty and meta = Jsont.Meta.none in
1313+ Jsont.Error.make_msg ctx meta (Jstr.to_string (Jv.Error.message e))
1414+1515+(* Browser JSON codec *)
1616+1717+let indent = Jstr.v " "
1818+let json = Jv.get Jv.global "JSON"
1919+let json_parse s = Jv.call json "parse" [|Jv.of_jstr s|]
2020+let json_stringify ~format v =
2121+ let args = match format with
2222+ | Jsont.Minify -> [| v |]
2323+ | Jsont.Indent | Jsont.Layout -> [|v; Jv.null; Jv.of_jstr indent|]
2424+ in
2525+ Jv.to_jstr (Jv.call json "stringify" args)
2626+2727+(* Computing the sort of a Jv.t value *)
2828+2929+let type_bool = Jstr.v "boolean"
3030+let type_object = Jstr.v "object"
3131+let type_number = Jstr.v "number"
3232+let type_string = Jstr.v "string"
3333+let type_array = Jv.get Jv.global "Array"
3434+3535+let jv_sort jv =
3636+ if Jv.is_null jv then Jsont.Sort.Null else
3737+ let t = Jv.typeof jv in
3838+ if Jstr.equal t type_bool then Jsont.Sort.Bool else
3939+ if Jstr.equal t type_number then Jsont.Sort.Number else
4040+ if Jstr.equal t type_string then Jsont.Sort.String else
4141+ if Jstr.equal t type_object
4242+ then (if Jv.is_array jv then Jsont.Sort.Array else Jsont.Sort.Object) else
4343+ Jsont.Error.msgf Jsont.Meta.none "Not a JSON value: %s" (Jstr.to_string t)
4444+4545+(* Getting the members of a Jv.t object in various ways *)
4646+4747+let jv_mem_names jv = Jv.call (Jv.get Jv.global "Object") "keys" [| jv |]
4848+let jv_mem_name_list jv = Jv.to_list Jv.to_string (jv_mem_names jv)
4949+let jv_mem_name_map : Jv.t -> Jstr.t String_map.t = fun jv ->
5050+ (* The map maps OCaml strings their corresponding JavaScript string *)
5151+ let rec loop ns i max m =
5252+ if i > max then m else
5353+ let n = Jv.Jarray.get ns i in
5454+ loop ns (i + 1) max (String_map.add (Jv.to_string n) (Jv.to_jstr n) m)
5555+ in
5656+ let ns = jv_mem_names jv in
5757+ loop ns 0 (Jv.Jarray.length ns - 1) String_map.empty
5858+5959+(* Decoding *)
6060+6161+let error_push_array map i e =
6262+ Jsont.Repr.error_push_array Jsont.Meta.none map (i, Jsont.Meta.none) e
6363+6464+let error_push_object map n e =
6565+ Jsont.Repr.error_push_object Jsont.Meta.none map (n, Jsont.Meta.none) e
6666+6767+let type_error t ~fnd =
6868+ Jsont.Repr.type_error Jsont.Meta.none t ~fnd
6969+7070+let find_all_unexpected ~mem_decs mems =
7171+ let unexpected (n, _jname) = match String_map.find_opt n mem_decs with
7272+ | None -> Some (n, Jsont.Meta.none) | Some _ -> None
7373+ in
7474+ List.filter_map unexpected mems
7575+7676+let rec decode : type a. a Jsont.Repr.t -> Jv.t -> a =
7777+fun t jv -> match t with
7878+| Null map ->
7979+ (match jv_sort jv with
8080+ | Null -> map.dec Jsont.Meta.none ()
8181+ | fnd -> type_error t ~fnd)
8282+| Bool map ->
8383+ (match jv_sort jv with
8484+ | Bool -> map.dec Jsont.Meta.none (Jv.to_bool jv)
8585+ | fnd -> type_error t ~fnd)
8686+| Number map ->
8787+ (match jv_sort jv with
8888+ | Number -> map.dec Jsont.Meta.none (Jv.to_float jv)
8989+ | Null -> map.dec Jsont.Meta.none Float.nan
9090+ | fnd -> type_error t ~fnd)
9191+| String map ->
9292+ (match jv_sort jv with
9393+ | String -> map.dec Jsont.Meta.none (Jv.to_string jv)
9494+ | fnd -> type_error t ~fnd)
9595+| Array map ->
9696+ (match jv_sort jv with
9797+ | Array -> decode_array map jv
9898+ | fnd -> type_error t ~fnd)
9999+| Object map ->
100100+ (match jv_sort jv with
101101+ | Object -> decode_object map jv
102102+ | fnd -> type_error t ~fnd)
103103+| Map map -> map.dec (decode map.dom jv)
104104+| Any map -> decode_any t map jv
105105+| Rec t -> decode (Lazy.force t) jv
106106+107107+and decode_array :
108108+ type a e b. (a, e, b) array_map -> Jv.t -> a
109109+=
110110+fun map jv ->
111111+ let len = Jv.Jarray.length jv in
112112+ let b = ref (map.dec_empty ()) in
113113+ for i = 0 to len - 1 do
114114+ try
115115+ if map.dec_skip i !b then () else
116116+ b := map.dec_add i (decode map.elt (Jv.Jarray.get jv i)) !b
117117+ with Jsont.Error e -> error_push_array map i e
118118+ done;
119119+ map.dec_finish Jsont.Meta.none len !b
120120+121121+and decode_object : type o. (o, o) object_map -> Jv.t -> o =
122122+fun map jv ->
123123+ let names = jv_mem_name_map jv in
124124+ let umems = Unknown_mems None in
125125+ let dict = decode_object_map map umems String_map.empty Dict.empty names jv in
126126+ apply_dict map.dec dict
127127+128128+and decode_object_map : type o.
129129+ (o, o) object_map -> unknown_mems_option -> mem_dec String_map.t -> Dict.t ->
130130+ Jstr.t String_map.t -> Jv.t -> Dict.t
131131+=
132132+fun map umems mem_decs dict names jv ->
133133+ let u _ _ _ = assert false (* They should be disjoint by contruction *) in
134134+ let mem_decs = String_map.union u mem_decs map.mem_decs in
135135+ match map.shape with
136136+ | Object_cases (umems', cases) ->
137137+ let umems' = Unknown_mems umems' in
138138+ let umems,dict = Jsont.Repr.override_unknown_mems ~by:umems umems' dict in
139139+ decode_object_cases map umems cases mem_decs dict names jv
140140+ | Object_basic umems' ->
141141+ let umems' = Unknown_mems (Some umems') in
142142+ let umems,dict = Jsont.Repr.override_unknown_mems ~by:umems umems' dict in
143143+ match umems with
144144+ | Unknown_mems (Some Unknown_skip | None) ->
145145+ let u = Unknown_skip in
146146+ decode_object_basic
147147+ map u () mem_decs dict (String_map.bindings names) jv
148148+ | Unknown_mems (Some (Unknown_error as u)) ->
149149+ decode_object_basic
150150+ map u () mem_decs dict (String_map.bindings names) jv
151151+ | Unknown_mems (Some (Unknown_keep (umap, _) as u)) ->
152152+ let umap = umap.dec_empty () and names = String_map.bindings names in
153153+ decode_object_basic map u umap mem_decs dict names jv
154154+155155+and decode_object_basic : type o p m b.
156156+ (o, o) object_map -> (p, m, b) unknown_mems -> b ->
157157+ mem_dec String_map.t -> Dict.t -> (string * Jstr.t) list -> Jv.t -> Dict.t
158158+=
159159+fun map umems umap mem_decs dict names jv -> match names with
160160+| [] ->
161161+ Jsont.Repr.finish_object_decode map Jsont.Meta.none umems umap mem_decs dict
162162+| (n, jname) :: names ->
163163+ match String_map.find_opt n mem_decs with
164164+ | Some (Mem_dec m) ->
165165+ let dict =
166166+ try Dict.add m.id (decode m.type' (Jv.get' jv jname)) dict with
167167+ | Jsont.Error e -> error_push_object map n e
168168+ in
169169+ let mem_decs = String_map.remove n mem_decs in
170170+ decode_object_basic map umems umap mem_decs dict names jv
171171+ | None ->
172172+ match umems with
173173+ | Unknown_skip ->
174174+ decode_object_basic map umems umap mem_decs dict names jv
175175+ | Unknown_error ->
176176+ let fnd =
177177+ (n, Jsont.Meta.none) :: find_all_unexpected ~mem_decs names
178178+ in
179179+ Jsont.Repr.unexpected_mems_error Jsont.Meta.none map ~fnd
180180+ | Unknown_keep (mmap, _) ->
181181+ let umap =
182182+ let v = try decode mmap.mems_type (Jv.get' jv jname) with
183183+ | Jsont.Error e -> error_push_object map n e
184184+ in
185185+ mmap.dec_add Jsont.Meta.none n v umap
186186+ in
187187+ decode_object_basic map umems umap mem_decs dict names jv
188188+189189+and decode_object_cases : type o cs t.
190190+ (o, o) object_map -> unknown_mems_option -> (o, cs, t) object_cases ->
191191+ mem_dec String_map.t -> Dict.t -> Jstr.t String_map.t -> Jv.t -> Dict.t
192192+=
193193+fun map umems cases mem_decs dict names jv ->
194194+ let decode_case_tag tag =
195195+ let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
196196+ match List.find_opt eq_tag cases.cases with
197197+ | None ->
198198+ Jsont.Repr.unexpected_case_tag_error Jsont.Meta.none map cases tag
199199+ | Some (Case case) ->
200200+ let mems = String_map.remove cases.tag.name names in
201201+ let dict =
202202+ decode_object_map case.object_map umems mem_decs dict mems jv
203203+ in
204204+ Dict.add cases.id (case.dec (apply_dict case.object_map.dec dict)) dict
205205+ in
206206+ match String_map.find_opt cases.tag.name names with
207207+ | Some jname ->
208208+ (try decode_case_tag (decode cases.tag.type' (Jv.get' jv jname)) with
209209+ | Jsont.Error e -> error_push_object map cases.tag.name e)
210210+ | None ->
211211+ match cases.tag.dec_absent with
212212+ | Some tag -> decode_case_tag tag
213213+ | None ->
214214+ let exp = String_map.singleton cases.tag.name (Mem_dec cases.tag) in
215215+ let fnd = jv_mem_name_list jv in
216216+ Jsont.Repr.missing_mems_error Jsont.Meta.none map ~exp ~fnd
217217+218218+and decode_any : type a. a t -> a any_map -> Jv.t -> a =
219219+fun t map jv ->
220220+ let case t map sort jv = match map with
221221+ | Some t -> decode t jv | None -> type_error t ~fnd:sort
222222+ in
223223+ match jv_sort jv with
224224+ | Null as s -> case t map.dec_null s jv
225225+ | Bool as s -> case t map.dec_bool s jv
226226+ | Number as s -> case t map.dec_number s jv
227227+ | String as s -> case t map.dec_string s jv
228228+ | Array as s -> case t map.dec_array s jv
229229+ | Object as s -> case t map.dec_object s jv
230230+231231+let decode t jv = decode (Jsont.Repr.of_t t) jv
232232+let decode_jv' t jv = try Ok (decode t jv) with Jsont.Error e -> Error e
233233+let decode_jv t jv = Result.map_error error_to_jv_error (decode_jv' t jv)
234234+let decode' t s = try Ok (decode t (json_parse s)) with
235235+| Jv.Error e -> Error (jv_error_to_error e) | Jsont.Error e -> Error e
236236+237237+let decode t json = Result.map_error error_to_jv_error (decode' t json)
238238+239239+(* Encoding *)
240240+241241+let rec encode : type a. a t -> a -> Jv.t =
242242+fun t v -> match t with
243243+| Null map -> map.enc v; Jv.null
244244+| Bool map -> Jv.of_bool (map.enc v)
245245+| Number map -> Jv.of_float (map.enc v)
246246+| String map -> Jv.of_string (map.enc v)
247247+| Array map ->
248248+ let add map a i vi = try Jv.Jarray.set a i (encode map.elt vi); a with
249249+ | Jsont.Error e -> error_push_array map i e
250250+ in
251251+ map.enc (add map) (Jv.Jarray.create 0) v
252252+| Object map -> encode_object map ~do_unknown:true v (Jv.obj [||])
253253+| Any map -> encode (map.enc v) v
254254+| Map map -> encode map.dom (map.enc v)
255255+| Rec t -> encode (Lazy.force t) v
256256+257257+and encode_object :
258258+ type o. (o, o) Jsont.Repr.object_map -> do_unknown:bool -> o -> Jv.t -> Jv.t
259259+=
260260+fun map ~do_unknown o jv ->
261261+ let encode_mem map o jv (Mem_enc mmap) =
262262+ try
263263+ let v = mmap.enc o in
264264+ if mmap.enc_omit v then jv else
265265+ (Jv.set' jv (Jstr.of_string mmap.name) (encode mmap.type' v); jv)
266266+ with
267267+ | Jsont.Error e -> error_push_object map mmap.name e
268268+ in
269269+ let jv = List.fold_left (encode_mem map o) jv map.mem_encs in
270270+ match map.shape with
271271+ | Object_basic (Unknown_keep (umap, enc)) when do_unknown ->
272272+ encode_unknown_mems map umap (enc o) jv
273273+ | Object_basic _ -> jv
274274+ | Object_cases (u, cases) ->
275275+ let Case_value (case, v) = cases.enc_case (cases.enc o) in
276276+ let jv =
277277+ try
278278+ if cases.tag.enc_omit case.tag then jv else
279279+ let tag = encode cases.tag.type' case.tag in
280280+ Jv.set' jv (Jstr.of_string cases.tag.name) tag; jv
281281+ with
282282+ | Jsont.Error e -> error_push_object map cases.tag.name e
283283+ in
284284+ match u with
285285+ | Some (Unknown_keep (umap, enc)) ->
286286+ (* Feels nicer to encode unknowns at the end *)
287287+ let jv = encode_object case.object_map ~do_unknown:false v jv in
288288+ encode_unknown_mems map umap (enc o) jv
289289+ | _ -> encode_object case.object_map ~do_unknown v jv
290290+291291+and encode_unknown_mems : type o mems a builder.
292292+ (o, o) object_map -> (mems, a, builder) mems_map -> mems -> Jv.t -> Jv.t =
293293+fun map umap mems jv ->
294294+ let encode_mem map meta name v jv =
295295+ try Jv.set' jv (Jstr.of_string name) (encode umap.mems_type v); jv with
296296+ | Jsont.Error e -> error_push_object map name e
297297+ in
298298+ umap.enc (encode_mem map) mems jv
299299+300300+let encode t v = encode (Jsont.Repr.of_t t) v
301301+let encode_jv' t v = try Ok (encode t v) with Jsont.Error e -> Error e
302302+let encode_jv t v = Result.map_error error_to_jv_error (encode_jv' t v)
303303+let encode' ?(format = Jsont.Minify) t v =
304304+ try Ok (json_stringify ~format (encode t v)) with
305305+ | Jv.Error e -> Error (jv_error_to_error e)
306306+ | Jsont.Error e -> Error e
307307+308308+let encode ?format t v =
309309+ Result.map_error error_to_jv_error (encode' ?format t v)
310310+311311+(* Recode *)
312312+313313+let recode ?format t s = match decode t s with
314314+| Error _ as e -> e | Ok v -> encode ?format t v
315315+316316+let recode' ?format t s = match decode' t s with
317317+| Error _ as e -> e | Ok v -> encode' ?format t v
318318+319319+let recode_jv t jv = match decode_jv t jv with
320320+| Error _ as e -> e | Ok v -> encode_jv t v
321321+322322+let recode_jv' t s = match decode_jv' t s with
323323+| Error _ as e -> e | Ok v -> encode_jv' t v
+68
vendor/opam/jsont/src/brr/jsont_brr.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** JavaScript support.
77+88+ {b Note.} These functions incur a bit of overhead but should work
99+ fast enough for medium sized structures. Get in touch if you run
1010+ into problems, some improvements may be possible.
1111+1212+ The JSON functions use JavaScript's
1313+ {{:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse}[JSON.parse]} and
1414+ {{:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify}[JSON.stringify]} to convert to JavaScript values
1515+ which are then converted with {!decode_jv} and {!encode_jv}. Parse
1616+ locations and layout preservation are unsupported. *)
1717+1818+(** {1:decode Decode} *)
1919+2020+val decode : 'a Jsont.t -> Jstr.t -> ('a, Jv.Error.t) result
2121+(** [decode t s] decodes the JSON data [s] according to [t]. *)
2222+2323+val decode' : 'a Jsont.t -> Jstr.t -> ('a, Jsont.Error.t) result
2424+(** [decode' t s] is like {!val-decode} but preserves the error structure. *)
2525+2626+val decode_jv : 'a Jsont.t -> Jv.t -> ('a, Jv.Error.t) result
2727+(** [decode_jv t v] decodes the JavaScript value [v] according to [t]. *)
2828+2929+val decode_jv' : 'a Jsont.t -> Jv.t -> ('a, Jsont.Error.t) result
3030+(** [decode_jv'] is like {!decode_jv'} but preserves the error structure. *)
3131+3232+(** {1:encode Encode} *)
3333+3434+val encode :
3535+ ?format:Jsont.format -> 'a Jsont.t -> 'a -> (Jstr.t, Jv.Error.t) result
3636+(** [encode t v] encodes [v] to JSON according to [t]. [format]
3737+ specifies how the JSON is formatted, defaults to
3838+ {!Jsont.Minify}. The {!Jsont.Layout} format is unsupported,
3939+ {!Jsont.Indent} is used instead. *)
4040+4141+val encode' :
4242+ ?format:Jsont.format -> 'a Jsont.t -> 'a -> (Jstr.t, Jsont.Error.t) result
4343+(** [encode'] is like {!val-encode} but preserves the error structure.
4444+ [format] specifies how the JSON is formatted, defaults to
4545+ {!Jsont.Minify}. The {!Jsont.Layout} format is unsupported,
4646+ {!Jsont.Indent} is used instead. *)
4747+4848+val encode_jv : 'a Jsont.t -> 'a -> (Jv.t, Jv.Error.t) result
4949+(** [encode_jv t v] encodes [v] to a JavaScript value according to [t]. *)
5050+5151+val encode_jv' : 'a Jsont.t -> 'a -> (Jv.t, Jsont.Error.t) result
5252+(** [encode_jv'] is like {!val-encode_jv} but preserves the error structure. *)
5353+5454+(** {1:recode Recode} *)
5555+5656+val recode : ?format:Jsont.format -> 'a Jsont.t -> Jstr.t ->
5757+ (Jstr.t, Jv.Error.t) result
5858+(** [recode] is {!val-decode} followed by {!val-encode}. *)
5959+6060+val recode' : ?format:Jsont.format -> 'a Jsont.t -> Jstr.t ->
6161+ (Jstr.t, Jsont.Error.t) result
6262+(** [recode] is {!val-decode'} followed by {!val-encode'}. *)
6363+6464+val recode_jv : 'a Jsont.t -> Jv.t -> (Jv.t, Jv.Error.t) result
6565+(** [recode] is {!val-decode} followed by {!val-encode}. *)
6666+6767+val recode_jv' : 'a Jsont.t -> Jv.t -> (Jv.t, Jsont.Error.t) result
6868+(** [recode] is {!val-decode_jv'} followed by {!encode_jv'}. *)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Bytesrw
77+open Jsont.Repr
88+99+(* XXX add these things to Stdlib.Uchar *)
1010+1111+let uchar_max_utf_8_byte_length = 4
1212+let[@inline] uchar_utf_8_byte_decode_length = function
1313+| '\x00' .. '\x7F' -> 1
1414+| '\x80' .. '\xC1' -> 0
1515+| '\xC2' .. '\xDF' -> 2
1616+| '\xE0' .. '\xEF' -> 3
1717+| '\xF0' .. '\xF4' -> 4
1818+| _ -> 0
1919+2020+(* Character classes *)
2121+2222+let[@inline] is_digit u = 0x0030 (* 0 *) <= u && u <= 0x0039 (* 9 *)
2323+let[@inline] is_number_start u = is_digit u || u = 0x002D (* - *)
2424+let[@inline] is_surrogate u = 0xD800 <= u && u <= 0xDFFF
2525+let[@inline] is_hi_surrogate u = 0xD800 <= u && u <= 0xDBFF
2626+let[@inline] is_lo_surrogate u = 0xDC00 <= u && u <= 0xDFFF
2727+let[@inline] is_control u =
2828+ (0x0000 <= u && u <= 0x001F) || (* C0 control characters *)
2929+ u = 0x007F || (* Delete *)
3030+ (0x0080 <= u && u <= 0x009F) || (* C1 control characters *)
3131+ u = 0x2028 (* Line separator *) ||
3232+ u = 0x2029 (* Paragraph separator *) ||
3333+ u = 0x200E (* left-to-right mark *) ||
3434+ u = 0x200F (* right-to-left mark *)
3535+3636+let sot = 0x1A0000 (* start of text U+10FFFF + 1 *)
3737+let eot = 0x1A0001 (* end of text U+10FFFF + 2 *)
3838+3939+let pp_code = Jsont.Repr.pp_code
4040+let pp_quchar ppf u =
4141+ pp_code ppf @@
4242+ if u = sot then "start of text" else
4343+ if u = eot then "end of text" else
4444+ if is_control u || is_surrogate u then Printf.sprintf "U+%04X" u else
4545+ let u = Uchar.of_int u in
4646+ let b = Stdlib.Bytes.make (Uchar.utf_8_byte_length u) '\x00' in
4747+ Stdlib.(ignore (Bytes.set_utf_8_uchar b 0 u); (Bytes.unsafe_to_string b))
4848+4949+(* Decoder *)
5050+5151+type decoder =
5252+ { file : string;
5353+ meta_none : Jsont.Meta.t; (* A meta with just [file] therein. *)
5454+ locs : bool; (* [true] if text locations should be computed. *)
5555+ layout : bool; (* [true] if text layout should be kept. *)
5656+ reader : Bytes.Reader.t; (* The source of bytes. *)
5757+ mutable i : Stdlib.Bytes.t; (* Current input slice. *)
5858+ mutable i_max : int; (* Maximum byte index in [i]. *)
5959+ mutable i_next : int; (* Next byte index to read in [i]. *)
6060+ mutable overlap : Stdlib.Bytes.t; (* Buffer for overlapping decodes. *)
6161+ mutable u : int; (* Current Unicode scalar value or sot or eot. *)
6262+ mutable byte_count : int; (* Global byte count. *)
6363+ mutable line : int; (* Current line number. *)
6464+ mutable line_start : int; (* Current line global byte position. *)
6565+ token : Buffer.t;
6666+ ws : Buffer.t; (* Bufferizes whitespace when layout is [true]. *) }
6767+6868+let make_decoder ?(locs = false) ?(layout = false) ?(file = "-") reader =
6969+ let overlap = Stdlib.Bytes.create uchar_max_utf_8_byte_length in
7070+ let token = Buffer.create 255 and ws = Buffer.create 255 in
7171+ let meta_none = Jsont.Meta.make (Jsont.Textloc.(set_file none) file) in
7272+ { file; meta_none; locs; layout; reader;
7373+ i = overlap (* overwritten by initial refill *);
7474+ i_max = 0; i_next = 1 (* triggers an initial refill *);
7575+ overlap; u = sot; byte_count = 0; line = 1; line_start = 0; token; ws }
7676+7777+(* Decoder positions *)
7878+7979+let[@inline] get_line_pos d = d.line, d.line_start
8080+8181+let get_last_byte d =
8282+ if d.u <= 0x7F then d.byte_count - 1 else
8383+ if d.u = sot || d.u = eot then d.byte_count else
8484+ (* On multi-bytes uchars we want to point on the first byte. *)
8585+ d.byte_count - Uchar.utf_8_byte_length (Uchar.of_int d.u)
8686+8787+(* Decoder errors *)
8888+8989+let error_meta d =
9090+ let first_byte = get_last_byte d and first_line = get_line_pos d in
9191+ let last_byte = first_byte and last_line = first_line in
9292+ Jsont.Meta.make @@
9393+ Jsont.Textloc.make ~file:d.file ~first_byte ~last_byte ~first_line ~last_line
9494+9595+let error_meta_to_current ~first_byte ~first_line d =
9696+ let last_byte = get_last_byte d and last_line = get_line_pos d in
9797+ Jsont.Meta.make @@
9898+ Jsont.Textloc.make ~file:d.file ~first_byte ~last_byte ~first_line ~last_line
9999+100100+let err_here d fmt = Jsont.Error.msgf (error_meta d) fmt
101101+let err_to_here ~first_byte ~first_line d fmt =
102102+ Jsont.Error.msgf (error_meta_to_current ~first_byte ~first_line d) fmt
103103+104104+let err_malformed_utf_8 d =
105105+ if d.i_next > d.i_max
106106+ then err_here d "UTF-8 decoding error: unexpected end of bytes"
107107+ else err_here d "UTF-8 decoding error: invalid byte %a"
108108+ pp_code (Printf.sprintf "%x02x" (Bytes.get_uint8 d.i d.i_next))
109109+110110+let err_exp d = err_here d "Expected %a but found %a"
111111+let err_exp_while d = err_here d "Expected %a while parsing %a but found %a"
112112+113113+let err_exp_eot d = err_exp d pp_quchar eot pp_quchar d.u
114114+let err_not_json_value d = err_exp d pp_code "JSON value" pp_quchar d.u
115115+116116+let current_json_sort d = match d.u with
117117+| 0x0066 (* f *) | 0x0074 (* t *) -> Jsont.Sort.Bool
118118+| 0x006E (* n *) -> Jsont.Sort.Null
119119+| 0x007B (* { *) -> Jsont.Sort.Object
120120+| 0x005B (* [ *) -> Jsont.Sort.Array
121121+| 0x0022 (* DQUOTE *) -> Jsont.Sort.String
122122+| u when is_number_start u -> Jsont.Sort.Number
123123+| _ -> err_not_json_value d
124124+125125+let type_error d t =
126126+ Jsont.Repr.type_error (error_meta d) t ~fnd:(current_json_sort d)
127127+128128+(* Errors for constants *)
129129+130130+let err_exp_in_const ~first_byte ~first_line d ~exp ~fnd ~const =
131131+ err_to_here ~first_byte ~first_line d
132132+ "Expected %a while parsing %a but found: %a"
133133+ pp_quchar exp pp_code const pp_quchar fnd
134134+135135+(* Errors for numbers *)
136136+137137+let err_float_parse meta tok =
138138+ Jsont.Error.msgf meta "Could not parse %S to a %a" tok pp_code "float"
139139+140140+let err_exp_digit d =
141141+ err_exp_while d pp_code "decimal digit" pp_code "number" pp_quchar d.u
142142+143143+(* Errors for strings *)
144144+145145+let err_exp_hex_digit d =
146146+ err_exp_while d pp_code "hex digit" pp_code "character escape" pp_quchar d.u
147147+148148+let err_exp_lo_surrogate d u =
149149+ err_exp_while d pp_code "low surrogate" pp_code "character escape" pp_quchar u
150150+151151+let err_unpaired_lo_surrogate d u =
152152+ err_here d "Unpaired low surrogate %a in %a" pp_quchar u pp_code "string"
153153+154154+let err_unpaired_hi_surrogate d u =
155155+ err_here d "Unpaired high surrogate %a in %a" pp_quchar u pp_code "string"
156156+157157+let err_exp_esc ~first_byte ~first_line d u =
158158+ err_to_here ~first_byte ~first_line d "Expected %a while parsing %a found %a"
159159+ pp_code "escape character" pp_code "escape" pp_quchar u
160160+161161+let err_unclosed_string ~first_byte ~first_line d =
162162+ err_to_here ~first_byte ~first_line d "Unclosed %a" pp_code "string"
163163+164164+let err_illegal_ctrl_char ~first_byte ~first_line d =
165165+ err_to_here ~first_byte ~first_line d "Illegal control character %a in %a"
166166+ pp_quchar d.u pp_code "string"
167167+168168+(* Errors for arrays *)
169169+170170+let err_exp_comma_or_eoa d ~fnd =
171171+ err_here d "Expected %a or %a after %a but found %a"
172172+ pp_code "," pp_code "]" pp_code "array element" pp_quchar fnd
173173+174174+let err_unclosed_array d = err_here d "Unclosed %a" pp_code "array"
175175+let err_exp_comma_or_eoo d =
176176+ err_here d "Expected %a or %a after %a but found: %a"
177177+ pp_code "," pp_code "}" pp_code "object member" pp_quchar d.u
178178+179179+(* Errors for objects *)
180180+181181+let err_exp_mem d =
182182+ err_here d "Expected %a but found %a"
183183+ pp_code "object member" pp_quchar d.u
184184+185185+let err_exp_mem_or_eoo d =
186186+ err_here d "Expected: %a or %a but found %a"
187187+ pp_code "object member" pp_code "}" pp_quchar d.u
188188+189189+let err_exp_colon d =
190190+ err_here d "Expected %a after %a but found %a"
191191+ pp_code ":" pp_code "member name" pp_quchar d.u
192192+193193+let err_unclosed_object d (map : ('o, 'o) Jsont.Repr.object_map) =
194194+ err_here d "Unclosed %a"
195195+ Jsont.Repr.pp_kind (Jsont.Repr.object_map_kinded_sort map)
196196+197197+(* Decode next character in d.u *)
198198+199199+let[@inline] is_eoslice d = d.i_next > d.i_max
200200+let[@inline] is_eod d = d.i_max = - 1 (* Only happens on Slice.eod *)
201201+let[@inline] available d = d.i_max - d.i_next + 1
202202+let[@inline] set_slice d slice =
203203+ d.i <- Bytes.Slice.bytes slice;
204204+ d.i_next <- Bytes.Slice.first slice;
205205+ d.i_max <- d.i_next + Bytes.Slice.length slice - 1
206206+207207+let rec setup_overlap d start need = match need with
208208+| 0 ->
209209+ let slice = match available d with
210210+ | 0 -> Bytes.Reader.read d.reader
211211+ | length -> Bytes.Slice.make d.i ~first:d.i_next ~length
212212+ in
213213+ d.i <- d.overlap; d.i_next <- 0; d.i_max <- start; slice
214214+| need ->
215215+ if is_eoslice d then set_slice d (Bytes.Reader.read d.reader);
216216+ if is_eod d
217217+ then (d.byte_count <- d.byte_count - start; err_malformed_utf_8 d);
218218+ let available = available d in
219219+ let take = Int.min need available in
220220+ for i = 0 to take - 1 do
221221+ Bytes.set d.overlap (start + i) (Bytes.get d.i (d.i_next + i))
222222+ done;
223223+ d.i_next <- d.i_next + take; d.byte_count <- d.byte_count + take;
224224+ setup_overlap d (start + take) (need - take)
225225+226226+let rec nextc d =
227227+ let a = available d in
228228+ if a <= 0 then
229229+ (if is_eod d
230230+ then d.u <- eot
231231+ else (set_slice d (Bytes.Reader.read d.reader); nextc d))
232232+ else
233233+ let b = Bytes.get d.i d.i_next in
234234+ if a < uchar_max_utf_8_byte_length &&
235235+ a < uchar_utf_8_byte_decode_length b then begin
236236+ let s = setup_overlap d 0 (uchar_utf_8_byte_decode_length b) in
237237+ nextc d; set_slice d s
238238+ end else
239239+ d.u <- match b with
240240+ | '\x00' .. '\x09' | '\x0B' | '\x0E' .. '\x7F' as u -> (* ASCII fast path *)
241241+ d.i_next <- d.i_next + 1; d.byte_count <- d.byte_count + 1;
242242+ Char.code u
243243+ | '\x0D' (* CR *) ->
244244+ d.i_next <- d.i_next + 1; d.byte_count <- d.byte_count + 1;
245245+ d.line_start <- d.byte_count; d.line <- d.line + 1;
246246+ 0x000D
247247+ | '\x0A' (* LF *) ->
248248+ d.i_next <- d.i_next + 1; d.byte_count <- d.byte_count + 1;
249249+ d.line_start <- d.byte_count;
250250+ if d.u <> 0x000D then d.line <- d.line + 1;
251251+ 0x000A
252252+ | _ ->
253253+ let udec = Bytes.get_utf_8_uchar d.i d.i_next in
254254+ if not (Uchar.utf_decode_is_valid udec) then err_malformed_utf_8 d else
255255+ let u = Uchar.to_int (Uchar.utf_decode_uchar udec) in
256256+ let ulen = Uchar.utf_decode_length udec in
257257+ d.i_next <- d.i_next + ulen; d.byte_count <- d.byte_count + ulen;
258258+ u
259259+260260+(* Decoder tokenizer *)
261261+262262+let[@inline] token_clear d = Buffer.clear d.token
263263+let[@inline] token_pop d = let t = Buffer.contents d.token in (token_clear d; t)
264264+let[@inline] token_add d u =
265265+ if u <= 0x7F
266266+ then Buffer.add_char d.token (Char.unsafe_chr u)
267267+ else Buffer.add_utf_8_uchar d.token (Uchar.unsafe_of_int u)
268268+269269+let[@inline] accept d = token_add d d.u; nextc d
270270+271271+let token_pop_float d ~meta =
272272+ let token = token_pop d in
273273+ match float_of_string_opt token with
274274+ | Some f -> f | None -> err_float_parse meta token (* likely [assert false] *)
275275+276276+(* Decoder layout and position tracking *)
277277+278278+let[@inline] ws_pop d =
279279+ if not d.layout then "" else
280280+ (let t = Buffer.contents d.ws in Buffer.clear d.ws; t)
281281+282282+let textloc_to_current ~first_byte ~first_line d =
283283+ if not d.locs then Jsont.Textloc.none else
284284+ let last_byte = get_last_byte d and last_line = get_line_pos d in
285285+ Jsont.Textloc.make ~file:d.file ~first_byte ~last_byte ~first_line ~last_line
286286+287287+let textloc_prev_ascii_char ~first_byte ~first_line d =
288288+ (* N.B. when we call that the line doesn't move and the char was on
289289+ a single byte *)
290290+ if not d.locs then Jsont.Textloc.none else
291291+ let last_byte = get_last_byte d and last_line = get_line_pos d in
292292+ let last_byte = last_byte - 1 in
293293+ Jsont.Textloc.make ~file:d.file ~first_byte ~last_byte ~first_line ~last_line
294294+295295+let meta_make d ?ws_before ?ws_after textloc =
296296+ if not d.locs && not d.layout then d.meta_none else
297297+ Jsont.Meta.make ?ws_before ?ws_after textloc
298298+299299+(* Decoding *)
300300+301301+let false_uchars = [| 0x0066; 0x0061; 0x006C; 0x0073; 0x0065 |]
302302+let true_uchars = [| 0x0074; 0x0072; 0x0075; 0x0065 |]
303303+let null_uchars = [| 0x006E; 0x0075; 0x006C; 0x006C |]
304304+let ascii_str us = String.init (Array.length us) (fun i -> Char.chr us.(i))
305305+306306+let[@inline] is_ws u =
307307+ if u > 0x20 then false else match Char.unsafe_chr u with
308308+ | ' ' | '\t' | '\r' | '\n' -> true
309309+ | _ -> false
310310+311311+let[@inline] read_ws d =
312312+ while is_ws d.u do
313313+ if d.layout then (Buffer.add_char d.ws (Char.unsafe_chr d.u));
314314+ nextc d
315315+ done
316316+317317+let read_json_const d const = (* First character was checked. *)
318318+ let ws_before = ws_pop d in
319319+ let first_byte = get_last_byte d and first_line = get_line_pos d in
320320+ for i = 1 to Array.length const - 1 do
321321+ nextc d;
322322+ if not (Int.equal d.u const.(i))
323323+ then err_exp_in_const ~first_byte ~first_line d ~exp:const.(i) ~fnd:d.u
324324+ ~const:(ascii_str const)
325325+ done;
326326+ let textloc = textloc_to_current d ~first_byte ~first_line in
327327+ let ws_after = (nextc d; read_ws d; ws_pop d) in
328328+ meta_make d ~ws_before ~ws_after textloc
329329+330330+let[@inline] read_json_false d = read_json_const d false_uchars
331331+let[@inline] read_json_true d = read_json_const d true_uchars
332332+let[@inline] read_json_null d = read_json_const d null_uchars
333333+let read_json_number d = (* [is_number_start d.u] = true *)
334334+ let[@inline] read_digits d = while is_digit d.u do accept d done in
335335+ let[@inline] read_int d = match d.u with
336336+ | 0x0030 (* 0 *) -> accept d
337337+ | u when is_digit u -> accept d; read_digits d
338338+ | u -> err_exp_digit d
339339+ in
340340+ let[@inline] read_opt_frac d = match d.u with
341341+ | 0x002E (* . *) ->
342342+ accept d; if is_digit d.u then read_digits d else err_exp_digit d
343343+ | _ -> ()
344344+ in
345345+ let[@inline] read_opt_exp d = match d.u with
346346+ | 0x0065 (* e *) | 0x0045 (* E *) ->
347347+ token_add d d.u; nextc d;
348348+ (match d.u with
349349+ | 0x002D (* - *) | 0x002B (* + *) -> token_add d d.u; nextc d
350350+ | _ -> ());
351351+ if is_digit d.u then read_digits d else err_exp_digit d
352352+ | _ -> ()
353353+ in
354354+ let first_byte = get_last_byte d in
355355+ let first_line = get_line_pos d in
356356+ let ws_before = ws_pop d in
357357+ token_clear d;
358358+ if d.u = 0x002D (* - *) then accept d;
359359+ read_int d;
360360+ read_opt_frac d;
361361+ read_opt_exp d;
362362+ let textloc = textloc_prev_ascii_char d ~first_byte ~first_line in
363363+ let ws_after = read_ws d; ws_pop d in
364364+ meta_make d ~ws_before ~ws_after textloc
365365+366366+let read_json_string d = (* d.u is 0x0022 *)
367367+ let first_byte = get_last_byte d and first_line = get_line_pos d in
368368+ let rec read_uescape d hi uc count =
369369+ if count > 0 then match d.u with
370370+ | u when 0x0030 <= u && u <= 0x0039 ->
371371+ nextc d; read_uescape d hi (uc * 16 + u - 0x30) (count - 1)
372372+ | u when 0x0041 <= u && u <= 0x0046 ->
373373+ nextc d; read_uescape d hi (uc * 16 + u - 0x37) (count - 1)
374374+ | u when 0x0061 <= u && u <= 0x0066 ->
375375+ nextc d; read_uescape d hi (uc * 16 + u - 0x57) (count - 1)
376376+ | u -> err_exp_hex_digit d
377377+ else match hi with
378378+ | Some hi -> (* combine high and low surrogate. *)
379379+ if not (is_lo_surrogate uc) then err_exp_lo_surrogate d uc else
380380+ let u = (((hi land 0x3FF) lsl 10) lor (uc land 0x3FF)) + 0x10000 in
381381+ token_add d u
382382+ | None ->
383383+ if not (is_surrogate uc) then token_add d uc else
384384+ if uc > 0xDBFF then err_unpaired_lo_surrogate d uc else
385385+ if d.u <> 0x005C (* \ *) then err_unpaired_hi_surrogate d uc else
386386+ (nextc d;
387387+ if d.u <> 0x0075 (* u *) then err_unpaired_hi_surrogate d uc else
388388+ (nextc d; read_uescape d (Some uc) 0 4))
389389+ in
390390+ let read_escape d = match d.u with
391391+ | 0x0022 (* DQUOTE *) | 0x005C (* \ *) | 0x002F (* / *) -> accept d
392392+ | 0x0062 (* b *) -> token_add d 0x0008 (* backspace *); nextc d
393393+ | 0x0066 (* f *) -> token_add d 0x000C (* form feed *); nextc d
394394+ | 0x006E (* n *) -> token_add d 0x000A (* line feed *); nextc d
395395+ | 0x0072 (* r *) -> token_add d 0x000D (* carriage return *); nextc d
396396+ | 0x0074 (* t *) -> token_add d 0x0009 (* tab *); nextc d
397397+ | 0x0075 (* u *) -> nextc d; read_uescape d None 0 4
398398+ | u -> err_exp_esc ~first_byte ~first_line d u
399399+ in
400400+ let rec loop d = match d.u with
401401+ | 0x005C (* \ *) -> nextc d; read_escape d; loop d
402402+ | 0x0022 (* DQUOTE *) -> ()
403403+ | u when u = eot -> err_unclosed_string ~first_byte ~first_line d
404404+ | u when 0x0000 <= u && u <= 0x001F ->
405405+ err_illegal_ctrl_char ~first_byte ~first_line d
406406+ | u -> accept d; loop d
407407+ in
408408+ let ws_before = ws_pop d in
409409+ nextc d; token_clear d; loop d;
410410+ let textloc = textloc_to_current d ~first_byte ~first_line in
411411+ let ws_after = nextc d; read_ws d; ws_pop d in
412412+ meta_make d ~ws_before ~ws_after textloc
413413+414414+let read_json_name d =
415415+ let meta = read_json_string d in
416416+ if d.u = 0x003A (* : *) then (nextc d; meta) else err_exp_colon d
417417+418418+let read_json_mem_sep d =
419419+ if d.u = 0x007D (* } *) then () else
420420+ if d.u = 0x002C (* , *)
421421+ then (nextc d; read_ws d; if d.u <> 0x0022 then err_exp_mem d)
422422+ else err_exp_comma_or_eoo d
423423+424424+let rec decode : type a. decoder -> a t -> a =
425425+fun d t -> match (read_ws d; t) with
426426+| Null map ->
427427+ (match d.u with
428428+ | 0x006E (* n *) -> map.dec (read_json_null d) ()
429429+ | _ -> type_error d t)
430430+| Bool map ->
431431+ (match d.u with
432432+ | 0x0066 (* f *) -> map.dec (read_json_false d) false
433433+ | 0x0074 (* t *) -> map.dec (read_json_true d) true
434434+ | _ -> type_error d t)
435435+| Number map ->
436436+ (match d.u with
437437+ | u when is_number_start u ->
438438+ let meta = read_json_number d in
439439+ map.dec meta (token_pop_float d ~meta)
440440+ | 0x006E (* n *) -> map.dec (read_json_null d) Float.nan
441441+ | _ -> type_error d t)
442442+| String map ->
443443+ (match d.u with
444444+ | 0x0022 (* DQUOTE *) ->
445445+ let meta = read_json_string d in
446446+ map.dec meta (token_pop d)
447447+ | _ -> type_error d t)
448448+| Array map ->
449449+ (match d.u with
450450+ | 0x005B (* [ *) -> decode_array d map
451451+ | _ -> type_error d t)
452452+| Object map ->
453453+ (match d.u with
454454+ | 0x007B (* { *) -> decode_object d map
455455+ | _ -> type_error d t)
456456+| Map map -> map.dec (decode d map.dom)
457457+| Any map -> decode_any d t map
458458+| Rec t -> decode d (Lazy.force t)
459459+460460+and decode_array : type a elt b. decoder -> (a, elt, b) array_map -> a =
461461+fun d map ->
462462+ let ws_before = ws_pop d in
463463+ let first_byte = get_last_byte d and first_line = get_line_pos d in
464464+ let b, len = match (nextc d; read_ws d; d.u) with
465465+ | 0x005D (* ] *) -> map.dec_empty (), 0
466466+ | _ ->
467467+ let b = ref (map.dec_empty ()) in
468468+ let i = ref 0 in
469469+ let next = ref true in
470470+ try
471471+ while !next do
472472+ begin
473473+ let first_byte = get_last_byte d and first_line = get_line_pos d in
474474+ try
475475+ if map.dec_skip !i !b
476476+ then (decode d (of_t Jsont.ignore))
477477+ else (b := map.dec_add !i (decode d map.elt) !b)
478478+ with
479479+ | Jsont.Error e ->
480480+ let imeta = error_meta_to_current ~first_byte ~first_line d in
481481+ Jsont.Repr.error_push_array (error_meta d) map (!i, imeta) e
482482+ end;
483483+ incr i;
484484+ match (read_ws d; d.u) with
485485+ | 0x005D (* ] *) -> next := false
486486+ | 0x002C (* , *) -> nextc d; read_ws d
487487+ | u when u = eot -> err_unclosed_array d
488488+ | fnd -> err_exp_comma_or_eoa d ~fnd
489489+ done;
490490+ !b, !i
491491+ with
492492+ | Jsont.Error e -> Jsont.Error.adjust_context ~first_byte ~first_line e
493493+ in
494494+ let textloc = textloc_to_current d ~first_byte ~first_line in
495495+ let ws_after = nextc d; read_ws d; ws_pop d in
496496+ let meta = meta_make d ~ws_before ~ws_after textloc in
497497+ map.dec_finish meta len b
498498+499499+and decode_object : type a. decoder -> (a, a) object_map -> a =
500500+fun d map ->
501501+ let ws_before = ws_pop d in
502502+ let first_byte = get_last_byte d and first_line = get_line_pos d in
503503+ let dict =
504504+ try
505505+ nextc d; read_ws d;
506506+ decode_object_map
507507+ d map (Unknown_mems None) String_map.empty String_map.empty []
508508+ Dict.empty
509509+ with
510510+ | Jsont.Error (ctx, meta, k) when Jsont.Error.Context.is_empty ctx ->
511511+ let meta =
512512+ (* This is for when Jsont.Repr.finish_object_decode raises. *)
513513+ if Jsont.Textloc.is_none (Jsont.Meta.textloc meta)
514514+ then error_meta_to_current d ~first_byte ~first_line
515515+ else meta
516516+ in
517517+ Jsont.Error.raise ctx meta k
518518+ | Jsont.Error e -> Jsont.Error.adjust_context ~first_byte ~first_line e
519519+ in
520520+ let textloc = textloc_to_current d ~first_byte ~first_line in
521521+ let ws_after = nextc d; read_ws d; ws_pop d in
522522+ let meta = meta_make d ~ws_before ~ws_after textloc in
523523+ let dict = Dict.add Jsont.Repr.object_meta_arg meta dict in
524524+ Jsont.Repr.apply_dict map.dec dict
525525+526526+and decode_object_delayed : type o.
527527+ decoder -> (o, o) object_map -> mem_dec String_map.t ->
528528+ mem_dec String_map.t -> Jsont.object' -> Dict.t ->
529529+ mem_dec String_map.t * Jsont.object' * Dict.t
530530+=
531531+fun d map mem_miss mem_decs delay dict ->
532532+ let rec loop d map mem_miss mem_decs rem_delay dict = function
533533+ | [] -> mem_miss, rem_delay, dict
534534+ | ((name, meta as nm), v as mem) :: delay ->
535535+ match String_map.find_opt name mem_decs with
536536+ | None -> loop d map mem_miss mem_decs (mem :: rem_delay) dict delay
537537+ | Some (Mem_dec m) ->
538538+ let dict =
539539+ try
540540+ let t = Jsont.Repr.unsafe_to_t m.type' in
541541+ let v = match Jsont.Json.decode' t v with
542542+ | Ok v -> v
543543+ | Error e -> raise_notrace (Jsont.Error e)
544544+ in
545545+ Dict.add m.id v dict
546546+ with
547547+ | Jsont.Error e ->
548548+ Jsont.Repr.error_push_object (error_meta d) map nm e
549549+ in
550550+ let mem_miss = String_map.remove name mem_miss in
551551+ loop d map mem_miss mem_decs rem_delay dict delay
552552+ in
553553+ loop d map mem_miss mem_decs [] dict delay
554554+555555+and decode_object_map : type o.
556556+ decoder -> (o, o) object_map -> unknown_mems_option ->
557557+ mem_dec String_map.t -> mem_dec String_map.t -> Jsont.object' -> Dict.t ->
558558+ Dict.t
559559+=
560560+fun d map umems mem_miss mem_decs delay dict ->
561561+ let u n _ _ = assert false in
562562+ let mem_miss = String_map.union u mem_miss map.mem_decs in
563563+ let mem_decs = String_map.union u mem_decs map.mem_decs in
564564+ match map.shape with
565565+ | Object_cases (umems', cases) ->
566566+ let umems' = Unknown_mems umems' in
567567+ let umems,dict = Jsont.Repr.override_unknown_mems ~by:umems umems' dict in
568568+ decode_object_case d map umems cases mem_miss mem_decs delay dict
569569+ | Object_basic umems' ->
570570+ let mem_miss, delay, dict =
571571+ decode_object_delayed d map mem_miss mem_decs delay dict
572572+ in
573573+ let umems' = Unknown_mems (Some umems') in
574574+ let umems,dict = Jsont.Repr.override_unknown_mems ~by:umems umems' dict in
575575+ match umems with
576576+ | Unknown_mems (Some Unknown_skip | None) ->
577577+ decode_object_basic d map Unknown_skip () mem_miss mem_decs dict
578578+ | Unknown_mems (Some (Unknown_error as u)) ->
579579+ if delay = []
580580+ then decode_object_basic d map u () mem_miss mem_decs dict else
581581+ let fnd = List.map fst delay in
582582+ Jsont.Repr.unexpected_mems_error (error_meta d) map ~fnd
583583+ | Unknown_mems (Some (Unknown_keep (umap, _) as u)) ->
584584+ let add_delay umems ((n, meta as nm), v) =
585585+ try
586586+ let t = Jsont.Repr.unsafe_to_t umap.mems_type in
587587+ let v = match Jsont.Json.decode' t v with
588588+ | Ok v -> v
589589+ | Error e -> raise_notrace (Jsont.Error e)
590590+ in
591591+ umap.dec_add meta n v umems
592592+ with
593593+ | Jsont.Error e ->
594594+ Jsont.Repr.error_push_object (error_meta d) map nm e
595595+ in
596596+ let umems = List.fold_left add_delay (umap.dec_empty ()) delay in
597597+ decode_object_basic d map u umems mem_miss mem_decs dict
598598+599599+and decode_object_basic : type o p mems builder.
600600+ decoder -> (o, o) object_map -> (p, mems, builder) unknown_mems -> builder ->
601601+ mem_dec String_map.t -> mem_dec String_map.t -> Dict.t -> Dict.t
602602+=
603603+fun d map u umap mem_miss mem_decs dict -> match d.u with
604604+| 0x007D (* } *) ->
605605+ let meta = d.meta_none (* we add a correct one in decode_object *) in
606606+ Jsont.Repr.finish_object_decode map meta u umap mem_miss dict
607607+| 0x0022 ->
608608+ let meta = read_json_name d in
609609+ let name = token_pop d in
610610+ begin match String_map.find_opt name mem_decs with
611611+ | Some (Mem_dec mem) ->
612612+ let mem_miss = String_map.remove name mem_miss in
613613+ let dict = try Dict.add mem.id (decode d mem.type') dict with
614614+ | Jsont.Error e ->
615615+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
616616+ in
617617+ read_json_mem_sep d;
618618+ decode_object_basic d map u umap mem_miss mem_decs dict
619619+ | None ->
620620+ match u with
621621+ | Unknown_skip ->
622622+ let () = try decode d (Jsont.Repr.of_t Jsont.ignore) with
623623+ | Jsont.Error e ->
624624+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
625625+ in
626626+ read_json_mem_sep d;
627627+ decode_object_basic d map u umap mem_miss mem_decs dict
628628+ | Unknown_error ->
629629+ let fnd = [name, meta] in
630630+ Jsont.Repr.unexpected_mems_error (error_meta d) map ~fnd
631631+ | Unknown_keep (umap', _) ->
632632+ let umap =
633633+ try umap'.dec_add meta name (decode d umap'.mems_type) umap with
634634+ | Jsont.Error e ->
635635+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
636636+ in
637637+ read_json_mem_sep d;
638638+ decode_object_basic d map u umap mem_miss mem_decs dict
639639+ end
640640+| u when u = eot -> err_unclosed_object d map
641641+| fnd -> err_exp_mem_or_eoo d
642642+643643+and decode_object_case : type o cases tag.
644644+ decoder -> (o, o) object_map -> unknown_mems_option ->
645645+ (o, cases, tag) object_cases -> mem_dec String_map.t ->
646646+ mem_dec String_map.t -> Jsont.object' -> Dict.t -> Dict.t
647647+=
648648+fun d map umems cases mem_miss mem_decs delay dict ->
649649+ let decode_case_tag ~sep map umems cases mem_miss mem_decs nmeta tag delay =
650650+ let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
651651+ match List.find_opt eq_tag cases.cases with
652652+ | None ->
653653+ (try Jsont.Repr.unexpected_case_tag_error (error_meta d) map cases tag
654654+ with Jsont.Error e ->
655655+ Jsont.Repr.error_push_object
656656+ (error_meta d) map (cases.tag.name, nmeta) e)
657657+ | Some (Case case) ->
658658+ if sep then read_json_mem_sep d;
659659+ let dict =
660660+ decode_object_map d case.object_map umems mem_miss mem_decs delay dict
661661+ in
662662+ Dict.add cases.id (case.dec (apply_dict case.object_map.dec dict)) dict
663663+ in
664664+ match d.u with
665665+ | 0x007D (* } *) ->
666666+ (match cases.tag.dec_absent with
667667+ | Some tag ->
668668+ decode_case_tag ~sep:false map umems cases mem_miss mem_decs
669669+ d.meta_none tag delay
670670+ | None ->
671671+ let fnd = (List.map (fun ((n, _), _) -> n) delay) in
672672+ let exp = String_map.singleton cases.tag.name (Mem_dec cases.tag) in
673673+ Jsont.Repr.missing_mems_error (error_meta d) map ~exp ~fnd)
674674+ | 0x0022 ->
675675+ let meta = read_json_name d in
676676+ let name = token_pop d in
677677+ if String.equal name cases.tag.name then
678678+ let tag = try decode d cases.tag.type' with
679679+ | Jsont.Error e ->
680680+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
681681+ in
682682+ decode_case_tag
683683+ ~sep:true map umems cases mem_miss mem_decs meta tag delay
684684+ else
685685+ begin match String_map.find_opt name mem_decs with
686686+ | Some (Mem_dec mem) ->
687687+ let mem_miss = String_map.remove name mem_miss in
688688+ let dict = try Dict.add mem.id (decode d mem.type') dict with
689689+ | Jsont.Error e ->
690690+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
691691+ in
692692+ read_json_mem_sep d;
693693+ decode_object_case d map umems cases mem_miss mem_decs delay dict
694694+ | None ->
695695+ (* Because JSON can be out of order we don't know how to decode
696696+ this yet. Generic decode *)
697697+ let v = try decode d (Jsont.Repr.of_t Jsont.json) with
698698+ | Jsont.Error e ->
699699+ Jsont.Repr.error_push_object (error_meta d) map (name, meta) e
700700+ in
701701+ let delay = ((name, meta), v) :: delay in
702702+ read_json_mem_sep d;
703703+ decode_object_case d map umems cases mem_miss mem_decs delay dict
704704+ end
705705+ | u when u = eot -> err_unclosed_object d map
706706+ | fnd -> err_exp_mem_or_eoo d
707707+708708+and decode_any : type a. decoder -> a t -> a any_map -> a =
709709+fun d t map ->
710710+ let case d t map = match map with
711711+ | None -> type_error d t | Some t -> decode d t
712712+ in
713713+ match d.u with
714714+ | 0x006E (* n *) -> case d t map.dec_null
715715+ | 0x0066 (* f *)
716716+ | 0x0074 (* t *) -> case d t map.dec_bool
717717+ | 0x0022 (* DQUOTE *) -> case d t map.dec_string
718718+ | 0x005B (* [ *) -> case d t map.dec_array
719719+ | 0x007B (* { *) -> case d t map.dec_object
720720+ | u when is_number_start u -> case d t map.dec_number
721721+ | _ -> err_not_json_value d
722722+723723+let decode' ?layout ?locs ?file t reader =
724724+ try
725725+ let d = make_decoder ?layout ?locs ?file reader in
726726+ let v = (nextc d; decode d (Jsont.Repr.of_t t)) in
727727+ if d.u <> eot then err_exp_eot d else Ok v
728728+ with Jsont.Error e -> Error e
729729+730730+let decode ?layout ?locs ?file t reader =
731731+ Result.map_error Jsont.Error.to_string (decode' ?layout ?locs ?file t reader)
732732+733733+let decode_string' ?layout ?locs ?file t s =
734734+ decode' ?layout ?locs ?file t (Bytes.Reader.of_string s)
735735+736736+let decode_string ?layout ?locs ?file t s =
737737+ decode ?layout ?locs ?file t (Bytes.Reader.of_string s)
738738+739739+(* Encoding *)
740740+741741+type encoder =
742742+ { writer : Bytes.Writer.t; (* Destination of bytes. *)
743743+ o : Bytes.t; (* Buffer for slices. *)
744744+ o_max : int; (* Max index in [o]. *)
745745+ mutable o_next : int; (* Next writable index in [o]. *)
746746+ format : Jsont.format;
747747+ number_format : string; }
748748+749749+let make_encoder
750750+ ?buf ?(format = Jsont.Minify) ?(number_format = Jsont.default_number_format)
751751+ writer
752752+ =
753753+ let o = match buf with
754754+ | Some buf -> buf
755755+ | None -> Bytes.create (Bytes.Writer.slice_length writer)
756756+ in
757757+ let len = Bytes.length o in
758758+ let number_format = string_of_format number_format in
759759+ let o_max = len - 1 and o_next = 0 in
760760+ { writer; o; o_max; o_next; format; number_format }
761761+762762+let[@inline] rem_len e = e.o_max - e.o_next + 1
763763+764764+let flush e =
765765+ Bytes.Writer.write e.writer (Bytes.Slice.make e.o ~first:0 ~length:e.o_next);
766766+ e.o_next <- 0
767767+768768+let write_eot ~eod e = flush e; if eod then Bytes.Writer.write_eod e.writer
769769+let write_char e c =
770770+ if e.o_next > e.o_max then flush e;
771771+ Stdlib.Bytes.set e.o e.o_next c; e.o_next <- e.o_next + 1
772772+773773+let rec write_substring e s first length =
774774+ if length = 0 then () else
775775+ let len = Int.min (rem_len e) length in
776776+ if len = 0 then (flush e; write_substring e s first length) else
777777+ begin
778778+ Bytes.blit_string s first e.o e.o_next len;
779779+ e.o_next <- e.o_next + len;
780780+ write_substring e s (first + len) (length - len)
781781+ end
782782+783783+let write_bytes e s = write_substring e s 0 (String.length s)
784784+let write_sep e = write_char e ','
785785+let write_indent e ~nest =
786786+ for i = 1 to nest do write_char e ' '; write_char e ' ' done
787787+788788+let write_ws_before e m = write_bytes e (Jsont.Meta.ws_before m)
789789+let write_ws_after e m = write_bytes e (Jsont.Meta.ws_after m)
790790+let write_json_null e = write_bytes e "null"
791791+let write_json_bool e b = write_bytes e (if b then "true" else "false")
792792+793793+(* XXX we bypass the printf machinery as it costs quite quite a bit.
794794+ Would be even better if we could format directly to a bytes values
795795+ rather than allocating a string per number. *)
796796+external format_float : string -> float -> string = "caml_format_float"
797797+let write_json_number e f =
798798+ if Float.is_finite f
799799+ then write_bytes e (format_float e.number_format f)
800800+ else write_json_null e
801801+802802+let write_json_string e s =
803803+ let is_control = function '\x00' .. '\x1F' | '\x7F' -> true | _ -> false in
804804+ let len = String.length s in
805805+ let flush e start i max =
806806+ if start <= max then write_substring e s start (i - start);
807807+ in
808808+ let rec loop start i max =
809809+ if i > max then flush e start i max else
810810+ let next = i + 1 in
811811+ match String.get s i with
812812+ | '\"' -> flush e start i max; write_bytes e "\\\""; loop next next max
813813+ | '\\' -> flush e start i max; write_bytes e "\\\\"; loop next next max
814814+ | '\n' -> flush e start i max; write_bytes e "\\n"; loop next next max
815815+ | '\r' -> flush e start i max; write_bytes e "\\r"; loop next next max
816816+ | '\t' -> flush e start i max; write_bytes e "\\t"; loop next next max
817817+ | c when is_control c ->
818818+ flush e start i max;
819819+ write_bytes e "\\u";
820820+ write_bytes e (Printf.sprintf "%04X" (Char.code c));
821821+ loop next next max
822822+ | c -> loop start next max
823823+ in
824824+ write_char e '"'; loop 0 0 (len - 1); write_char e '"'
825825+826826+let encode_null (map : ('a, 'b) Jsont.Repr.base_map) e v =
827827+ let () = map.enc v in
828828+ match e.format with
829829+ | Jsont.Minify | Jsont.Indent -> write_json_null e
830830+ | Jsont.Layout ->
831831+ let meta = map.enc_meta v in
832832+ write_ws_before e meta;
833833+ write_json_null e;
834834+ write_ws_after e meta
835835+836836+let encode_bool (map : ('a, 'b) Jsont.Repr.base_map) e v =
837837+ let b = map.enc v in
838838+ match e.format with
839839+ | Jsont.Minify | Jsont.Indent -> write_json_bool e b
840840+ | Jsont.Layout ->
841841+ let meta = map.enc_meta v in
842842+ write_ws_before e meta;
843843+ write_json_bool e b;
844844+ write_ws_after e meta
845845+846846+let encode_number (map : ('a, 'b) Jsont.Repr.base_map) e v =
847847+ let n = map.enc v in
848848+ match e.format with
849849+ | Jsont.Minify | Jsont.Indent -> write_json_number e n
850850+ | Jsont.Layout ->
851851+ let meta = map.enc_meta v in
852852+ write_ws_before e meta;
853853+ write_json_number e n;
854854+ write_ws_after e meta
855855+856856+let encode_string (map : ('a, 'b) Jsont.Repr.base_map) e v =
857857+ let s = map.enc v in
858858+ match e.format with
859859+ | Jsont.Minify | Jsont.Indent -> write_json_string e s
860860+ | Jsont.Layout ->
861861+ let meta = map.enc_meta v in
862862+ write_ws_before e meta;
863863+ write_json_string e s;
864864+ write_ws_after e meta
865865+866866+let encode_mem_indent ~nest e = write_char e '\n'; write_indent e ~nest
867867+let encode_mem_name e meta n = match e.format with
868868+ | Jsont.Minify -> write_json_string e n; write_char e ':'
869869+ | Jsont.Indent -> write_json_string e n; write_bytes e ": "
870870+ | Jsont.Layout ->
871871+ write_ws_before e meta;
872872+ write_json_string e n;
873873+ write_ws_after e meta;
874874+ write_char e ':'
875875+876876+let rec encode : type a. nest:int -> a Jsont.Repr.t -> encoder -> a -> unit =
877877+fun ~nest t e v -> match t with
878878+| Null map -> encode_null map e v
879879+| Bool map -> encode_bool map e v
880880+| Number map -> encode_number map e v
881881+| String map -> encode_string map e v
882882+| Array map -> encode_array ~nest map e v
883883+| Object map -> encode_object ~nest map e v
884884+| Any map -> encode ~nest (map.enc v) e v
885885+| Map map -> encode ~nest map.dom e (map.enc v)
886886+| Rec t -> encode ~nest (Lazy.force t) e v
887887+888888+and encode_array : type a elt b.
889889+ nest:int -> (a, elt, b) Jsont.Repr.array_map -> encoder -> a -> unit
890890+=
891891+fun ~nest map e v ->
892892+ let encode_element ~nest map e i v =
893893+ if i <> 0 then write_sep e;
894894+ try encode ~nest map.elt e v; e with
895895+ | Jsont.Error e ->
896896+ Jsont.Repr.error_push_array Jsont.Meta.none map (i, Jsont.Meta.none) e
897897+ in
898898+ match e.format with
899899+ | Jsont.Minify ->
900900+ write_char e '[';
901901+ ignore (map.enc (encode_element ~nest:(nest + 1) map) e v);
902902+ write_char e ']'
903903+ | Jsont.Layout ->
904904+ let meta = map.enc_meta v in
905905+ write_ws_before e meta;
906906+ write_char e '[';
907907+ ignore (map.enc (encode_element ~nest:(nest + 1) map) e v);
908908+ write_char e ']';
909909+ write_ws_after e meta
910910+ | Jsont.Indent ->
911911+ let encode_element ~nest map e i v =
912912+ if i <> 0 then write_sep e;
913913+ write_char e '\n';
914914+ write_indent e ~nest;
915915+ try encode ~nest map.elt e v; e with
916916+ | Jsont.Error e ->
917917+ Jsont.Repr.error_push_array
918918+ Jsont.Meta.none map (i, Jsont.Meta.none) e
919919+ in
920920+ let array_not_empty e =
921921+ e.o_next = 0 || not (Bytes.get e.o (e.o_next - 1) = '[')
922922+ in
923923+ write_char e '[';
924924+ ignore (map.enc (encode_element ~nest:(nest + 1) map) e v);
925925+ if array_not_empty e then (write_char e '\n'; write_indent e ~nest);
926926+ write_char e ']'
927927+928928+and encode_object : type o enc.
929929+ nest:int -> (o, o) Jsont.Repr.object_map -> encoder -> o -> unit
930930+ =
931931+ fun ~nest map e o -> match e.format with
932932+ | Jsont.Minify ->
933933+ write_char e '{';
934934+ ignore @@
935935+ encode_object_map ~nest:(nest + 1) map ~do_unknown:true e ~start:true o;
936936+ write_char e '}';
937937+ | Jsont.Layout ->
938938+ let meta = map.enc_meta o in
939939+ write_ws_before e meta;
940940+ write_char e '{';
941941+ ignore @@
942942+ encode_object_map ~nest:(nest + 1) map ~do_unknown:true e ~start:true o;
943943+ write_char e '}';
944944+ write_ws_after e meta;
945945+ | Jsont.Indent ->
946946+ write_char e '{';
947947+ let start =
948948+ encode_object_map ~nest:(nest + 1) map ~do_unknown:true e ~start:true o
949949+ in
950950+ if not start then (write_char e '\n'; write_indent e ~nest);
951951+ write_char e '}'
952952+953953+and encode_object_map : type o enc.
954954+ nest:int -> (o, o) Jsont.Repr.object_map -> do_unknown:bool -> encoder ->
955955+ start:bool -> o -> bool
956956+=
957957+fun ~nest map ~do_unknown e ~start o ->
958958+ let encode_mem ~nest map e o start (Mem_enc mmap) =
959959+ try
960960+ let v = mmap.enc o in
961961+ if mmap.enc_omit v then start else
962962+ begin
963963+ if not start then write_char e ',';
964964+ if e.format = Jsont.Indent then encode_mem_indent ~nest e;
965965+ let meta =
966966+ (* if e.format = Jsont.Layout then mmap.enc_name_meta v else *)
967967+ Jsont.Meta.none
968968+ in
969969+ encode_mem_name e meta mmap.name;
970970+ encode ~nest mmap.type' e v;
971971+ false
972972+ end
973973+ with
974974+ | Jsont.Error e ->
975975+ Jsont.Repr.error_push_object Jsont.Meta.none map
976976+ (mmap.name, Jsont.Meta.none) e
977977+ in
978978+ match map.shape with
979979+ | Object_basic u ->
980980+ let start =
981981+ List.fold_left (encode_mem ~nest map e o) start map.mem_encs
982982+ in
983983+ begin match u with
984984+ | Unknown_keep (umap, enc) when do_unknown ->
985985+ encode_unknown_mems ~nest map umap e ~start (enc o)
986986+ | _ -> start
987987+ end
988988+ | Object_cases (umap, cases) ->
989989+ let Case_value (case, c) = cases.enc_case (cases.enc o) in
990990+ let start =
991991+ if cases.tag.enc_omit case.tag
992992+ then start
993993+ else encode_mem ~nest map e case.tag start (Mem_enc cases.tag)
994994+ in
995995+ let start =
996996+ List.fold_left (encode_mem ~nest map e o) start map.mem_encs
997997+ in
998998+ match umap with
999999+ | Some (Unknown_keep (umap, enc)) ->
10001000+ let start =
10011001+ encode_object_map ~nest case.object_map ~do_unknown:false e ~start c
10021002+ in
10031003+ encode_unknown_mems ~nest map umap e ~start (enc o)
10041004+ | _ ->
10051005+ encode_object_map ~nest case.object_map ~do_unknown e ~start c
10061006+10071007+and encode_unknown_mems : type o dec mems a builder.
10081008+ nest:int -> (o,o) object_map -> (mems, a, builder) mems_map ->
10091009+ encoder -> start:bool -> mems -> bool
10101010+=
10111011+fun ~nest map umap e ~start mems ->
10121012+ let encode_unknown_mem ~nest map umap e meta n v start =
10131013+ try
10141014+ if not start then write_char e ',';
10151015+ if e.format = Jsont.Indent then encode_mem_indent ~nest e;
10161016+ encode_mem_name e meta n;
10171017+ encode ~nest umap.mems_type e v; false
10181018+ with
10191019+ | Jsont.Error e ->
10201020+ Jsont.Repr.error_push_object Jsont.Meta.none map (n, Jsont.Meta.none) e
10211021+ in
10221022+ umap.enc (encode_unknown_mem ~nest map umap e) mems start
10231023+10241024+let encode' ?buf ?format ?number_format t v ~eod w =
10251025+ let e = make_encoder ?buf ?format ?number_format w in
10261026+ let t = Jsont.Repr.of_t t in
10271027+ try Ok (encode ~nest:0 t e v; write_eot ~eod e) with
10281028+ | Jsont.Error e -> Error e
10291029+10301030+let encode ?buf ?format ?number_format t v ~eod w =
10311031+ Result.map_error Jsont.Error.to_string @@
10321032+ encode' ?buf ?format ?number_format ~eod t v w
10331033+10341034+let encode_string' ?buf ?format ?number_format t v =
10351035+ let b = Buffer.create 255 in
10361036+ let w = Bytes.Writer.of_buffer b in
10371037+ match encode' ?buf ?format ?number_format ~eod:true t v w with
10381038+ | Ok () -> Ok (Buffer.contents b) | Error _ as e -> e
10391039+10401040+let encode_string ?buf ?format ?number_format t v =
10411041+ Result.map_error Jsont.Error.to_string @@
10421042+ encode_string' ?buf ?format ?number_format t v
10431043+10441044+(* Recode *)
10451045+10461046+let unsurprising_defaults layout format = match layout, format with
10471047+| Some true, None -> Some true, Some Jsont.Layout
10481048+| None, (Some Jsont.Layout as l) -> Some true, l
10491049+| l, f -> l, f
10501050+10511051+let recode' ?layout ?locs ?file ?buf ?format ?number_format t r w ~eod =
10521052+ let layout, format = unsurprising_defaults layout format in
10531053+ match decode' ?layout ?locs ?file t r with
10541054+ | Error _ as e -> e
10551055+ | Ok v -> encode' ?buf ?format ?number_format t v ~eod w
10561056+10571057+let recode ?layout ?locs ?file ?buf ?format ?number_format t r w ~eod =
10581058+ Result.map_error Jsont.Error.to_string @@
10591059+ recode' ?layout ?locs ?file ?buf ?format ?number_format t r w ~eod
10601060+10611061+let recode_string' ?layout ?locs ?file ?buf ?format ?number_format t s =
10621062+ let layout, format = unsurprising_defaults layout format in
10631063+ match decode_string' ?layout ?locs ?file t s with
10641064+ | Error _ as e -> e
10651065+ | Ok v -> encode_string' ?buf ?format ?number_format t v
10661066+10671067+let recode_string ?layout ?locs ?file ?buf ?format ?number_format t s =
10681068+ Result.map_error Jsont.Error.to_string @@
10691069+ recode_string' ?layout ?locs ?file ?buf ?format ?number_format t s
+128
vendor/opam/jsont/src/bytesrw/jsont_bytesrw.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** JSON codec.
77+88+ According to {{:https://www.rfc-editor.org/rfc/rfc8259}RFC 8259}.
99+1010+ See notes about {{!layout}layout preservation} and behaviour
1111+ on {{!duplicate}duplicate members}.
1212+1313+ {b Tip.} For maximal performance decode with [~layout:false] and
1414+ [~locs:false], this is the default. Howver using [~locs:true] improves
1515+ some error reports. *)
1616+1717+open Bytesrw
1818+1919+(** {1:decode Decode} *)
2020+2121+val decode :
2222+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> 'a Jsont.t ->
2323+ Bytes.Reader.t -> ('a, string) result
2424+(** [decode t r] decodes a value from [r] according to [t].
2525+ {ul
2626+ {- If [layout] is [true] whitespace is preserved in {!Jsont.Meta.t}
2727+ values. Defaults to [false].}
2828+ {- If [locs] is [true] locations are preserved in {!Jsont.Meta.t}
2929+ values and error messages are precisely located. Defaults to [false].}
3030+ {- [file] is the file path from which [r] is assumed to read.
3131+ Defaults to {!Jsont.Textloc.file_none}}} *)
3232+3333+val decode' :
3434+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> 'a Jsont.t ->
3535+ Bytes.Reader.t -> ('a, Jsont.Error.t) result
3636+(** [decode'] is like {!val-decode} but preserves the error structure. *)
3737+3838+val decode_string :
3939+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> 'a Jsont.t ->
4040+ string -> ('a, string) result
4141+(** [decode_string] is like {!val-decode} but decodes directly from a string. *)
4242+4343+val decode_string' :
4444+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> 'a Jsont.t ->
4545+ string -> ('a, Jsont.Error.t) result
4646+(** [decode_string'] is like {!val-decode'} but decodes directly from a
4747+ string. *)
4848+4949+(** {1:encode Encode} *)
5050+5151+val encode :
5252+ ?buf:Bytes.t -> ?format:Jsont.format -> ?number_format:Jsont.number_format ->
5353+ 'a Jsont.t -> 'a -> eod:bool -> Bytes.Writer.t -> (unit, string) result
5454+(** [encode t v w] encodes value [v] according to [t] on [w].
5555+ {ul
5656+ {- If [buf] is specified it is used as a buffer for the slices written
5757+ on [w]. Defaults to a buffer of length {!Bytes.Writer.slice_length}[ w].}
5858+ {- [format] specifies how the JSON should be formatted.
5959+ Defaults to {!Jsont.Minify}.}
6060+ {- [number_format] specifies the format string to format numbers. Defaults
6161+ to {!Jsont.default_number_format}.}
6262+ {- [eod] indicates whether {!Bytesrw.Bytes.Slice.eod} should
6363+ be written on [w].}} *)
6464+6565+val encode' :
6666+ ?buf:Bytes.t -> ?format:Jsont.format -> ?number_format:Jsont.number_format ->
6767+ 'a Jsont.t -> 'a -> eod:bool -> Bytes.Writer.t -> (unit, Jsont.Error.t) result
6868+(** [encode'] is like {!val-encode} but preserves the error structure. *)
6969+7070+val encode_string :
7171+ ?buf:Bytes.t -> ?format:Jsont.format -> ?number_format:Jsont.number_format ->
7272+ 'a Jsont.t -> 'a -> (string, string) result
7373+(** [encode_string] is like {!val-encode} but writes to a string. *)
7474+7575+val encode_string' :
7676+ ?buf:Bytes.t -> ?format:Jsont.format -> ?number_format:Jsont.number_format ->
7777+ 'a Jsont.t -> 'a -> (string, Jsont.Error.t) result
7878+(** [encode_string'] is like {!val-encode'} but writes to a string. *)
7979+8080+(** {1:recode Recode}
8181+8282+ The defaults in these functions are those of {!val-decode} and
8383+ {!val-encode}, except if [layout] is [true], [format] defaults to
8484+ [Jsont.Layout] and vice-versa. *)
8585+8686+val recode :
8787+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> ?buf:Bytes.t ->
8888+ ?format:Jsont.format -> ?number_format:Jsont.number_format -> 'a Jsont.t ->
8989+ Bytes.Reader.t -> Bytes.Writer.t -> eod:bool -> (unit, string) result
9090+(** [recode] is {!val-decode} followed by {!val-recode}. *)
9191+9292+val recode' :
9393+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> ?buf:Bytes.t ->
9494+ ?format:Jsont.format -> ?number_format:Jsont.number_format -> 'a Jsont.t ->
9595+ Bytes.Reader.t -> Bytes.Writer.t -> eod:bool -> (unit, Jsont.Error.t) result
9696+(** [recode'] is like {!val-recode} but preserves the error structure. *)
9797+9898+val recode_string :
9999+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> ?buf:Bytes.t ->
100100+ ?format:Jsont.format -> ?number_format:Jsont.number_format -> 'a Jsont.t ->
101101+ string -> (string, string) result
102102+(** [recode] is {!decode_string} followed by {!recode_string}. *)
103103+104104+val recode_string' :
105105+ ?layout:bool -> ?locs:bool -> ?file:Jsont.Textloc.fpath -> ?buf:Bytes.t ->
106106+ ?format:Jsont.format -> ?number_format:Jsont.number_format -> 'a Jsont.t ->
107107+ string -> (string, Jsont.Error.t) result
108108+(** [recode_string'] is like {!val-recode_string} but preserves the error
109109+ structure. *)
110110+111111+(** {1:layout Layout preservation}
112112+113113+ In order to simplify the implementation not all layout is preserved.
114114+ In particular:
115115+ {ul
116116+ {- White space in empty arrays and objects is dropped.}
117117+ {- Unicode escapes are replaced by their UTF-8 encoding.}
118118+ {- The format of numbers is not preserved.}} *)
119119+120120+(** {1:duplicate Duplicate object members}
121121+122122+ Duplicate object members are undefined behaviour in JSON. We
123123+ follow the behaviour of
124124+ {{:https://262.ecma-international.org/6.0/#sec-internalizejsonproperty}
125125+ [JSON.parse]} and the last one takes over, however duplicate
126126+ members all have to parse with the specified type as we error as soon
127127+ as possible. Also
128128+ {{!Jsont.Object.case_mem}case members} are not allowed to duplicate. *)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+module Fmt = Jsont_base.Fmt
77+type 'a fmt = 'a Fmt.t
88+let pp_kind = Fmt.code
99+let pp_kind_opt ppf kind = if kind = "" then () else pp_kind ppf kind
1010+let pp_name = Fmt.code
1111+let pp_int ppf i = Fmt.code ppf (Int.to_string i)
1212+1313+module Textloc = Jsont_base.Textloc
1414+module Meta = Jsont_base.Meta
1515+type 'a node = 'a * Meta.t
1616+1717+module Path = Jsont_base.Path
1818+module Sort = Jsont_base.Sort
1919+2020+type error_kind = string
2121+type context_index = string node * Path.index
2222+type context = context_index list
2323+type error = context * Meta.t * error_kind
2424+exception Error of error
2525+2626+module Error = struct
2727+2828+ (* Kinds of errors *)
2929+3030+ type kind = error_kind
3131+ let kind_to_string k = k
3232+3333+ (* Errors *)
3434+3535+ module Context = struct
3636+ type index = context_index
3737+ type t = context
3838+ let empty = []
3939+ let is_empty ctx = ctx = []
4040+ let push_array kinded_sort n ctx = (kinded_sort, Path.Nth n) :: ctx
4141+ let push_object kinded_sort n ctx = (kinded_sort, Path.Mem n) :: ctx
4242+ let pp ppf ctx =
4343+ let pp_meta ppf meta =
4444+ if Meta.is_none meta then () else
4545+ Fmt.pf ppf "%a: " Textloc.pp (Meta.textloc meta)
4646+ in
4747+ let pp_el ppf (kind, index) = match index with
4848+ | Path.Nth (n, meta) ->
4949+ Fmt.pf ppf "@[<v>%aat index %a of@,%a%a@]"
5050+ pp_meta meta pp_int n
5151+ pp_meta (snd kind) pp_kind (fst kind)
5252+ | Path.Mem (name, meta) ->
5353+ Fmt.pf ppf "@[<v>%ain member %a of@,%a%a@]"
5454+ pp_meta meta pp_name name
5555+ pp_meta (snd kind) pp_kind (fst kind)
5656+ in
5757+ if ctx = [] then () else
5858+ Fmt.pf ppf "@,@[<v>%a@]" (Fmt.list pp_el) (List.rev ctx)
5959+ end
6060+6161+ type t = error
6262+6363+ let make_msg ctx meta msg = ctx, meta, msg
6464+ let raise ctx meta msg = raise_notrace (Error (ctx, meta, msg))
6565+ let msg meta msg = raise_notrace (Error (Context.empty, meta, msg))
6666+ let msgf meta fmt = Format.kasprintf (fun m -> msg meta m) fmt
6767+ let push_array kinded_sort n (ctx, meta, e) =
6868+ raise_notrace (Error (Context.push_array kinded_sort n ctx, meta, e))
6969+7070+ let push_object kinded_sort n (ctx, meta, e) =
7171+ raise_notrace (Error (Context.push_object kinded_sort n ctx, meta, e))
7272+7373+ let adjust_context ~first_byte ~first_line (ctx, meta, e) = match ctx with
7474+ | [] -> raise_notrace (Error (ctx, meta, e))
7575+ | ((sort, smeta), idx) :: is ->
7676+ let textloc = Meta.textloc smeta in
7777+ let textloc =
7878+ if Textloc.is_none textloc then textloc else
7979+ Textloc.set_first textloc ~first_byte ~first_line
8080+ in
8181+ let smeta = Meta.with_textloc smeta textloc in
8282+ let ctx = ((sort, smeta), idx) :: is in
8383+ raise_notrace (Error (ctx, meta, e))
8484+8585+ let pp ppf (ctx, m, msg) =
8686+ let pp_meta ppf m =
8787+ if not (Meta.is_none m)
8888+ then Fmt.pf ppf "@,%a:" Textloc.pp (Meta.textloc m)
8989+ in
9090+ Fmt.pf ppf "@[<v>%a%a%a@]" Fmt.lines msg pp_meta m Context.pp ctx
9191+9292+ let to_string e = Format.asprintf "%a" pp e
9393+9494+ let puterr = Fmt.puterr
9595+ let disable_ansi_styler = Fmt.disable_ansi_styler
9696+9797+ (* Predefined errors *)
9898+9999+ let expected meta exp ~fnd =
100100+ msgf meta "Expected %a but found %a" Fmt.code exp Fmt.code fnd
101101+102102+ let sort meta ~exp ~fnd =
103103+ msgf meta "Expected %a but found %a" Sort.pp exp Sort.pp fnd
104104+105105+ let kinded_sort meta ~exp ~fnd =
106106+ msgf meta "Expected %a but found %a" Fmt.code exp Sort.pp fnd
107107+108108+ let missing_mems meta ~kinded_sort ~exp ~fnd =
109109+ let pp_miss ppf m =
110110+ Fmt.pf ppf "@[%a%a@]" Fmt.code m Fmt.similar_mems (m, fnd)
111111+ in
112112+ match exp with
113113+ | [n] ->
114114+ msgf meta "@[<v>Missing member %a in %a%a@]"
115115+ Fmt.code n Fmt.code kinded_sort Fmt.similar_mems (n, fnd)
116116+ | exp ->
117117+ msgf meta "@[<v1>Missing members in %a:@,%a@]"
118118+ Fmt.code kinded_sort (Fmt.list pp_miss) exp
119119+120120+ let unexpected_mems meta ~kinded_sort ~exp ~fnd =
121121+ let pp_unexp ppf m =
122122+ Fmt.pf ppf " @[%a%a@]" Fmt.code m Fmt.should_it_be_mem (m, exp)
123123+ in
124124+ match fnd with
125125+ | [(u, _)] ->
126126+ msgf meta "@[<v>Unexpected member %a for %a%a@]"
127127+ Fmt.code u Fmt.code kinded_sort Fmt.should_it_be_mem (u, exp)
128128+ | us ->
129129+ msgf meta "@[<v1>Unexpected members for %a:@,%a@]"
130130+ Fmt.code kinded_sort (Fmt.list pp_unexp) (List.map fst us)
131131+132132+ let unexpected_case_tag meta ~kinded_sort ~mem_name ~exp ~fnd =
133133+ let pp_kind ppf () =
134134+ Fmt.pf ppf "member %a value in %a" Fmt.code mem_name Fmt.code kinded_sort
135135+ in
136136+ msgf meta "@[%a@]" (Fmt.out_of_dom ~pp_kind ()) (fnd, exp)
137137+138138+ (* Numbers *)
139139+140140+ let index_out_of_range meta ~n ~len =
141141+ msgf meta "Index %a out of range [%a;%a]" pp_int n pp_int 0 pp_int (len - 1)
142142+143143+ let number_range meta ~kind n =
144144+ msgf meta "Number %a not in %a range"
145145+ Fmt.code (Fmt.str "%a" Fmt.json_number n) Fmt.code kind
146146+147147+ let parse_string_number meta ~kind s =
148148+ msgf meta "String %a does not parse to %a value"
149149+ Fmt.json_string s pp_kind kind
150150+151151+ let integer_range meta ~kind n =
152152+ msgf meta "Integer %a not in %a range" pp_int n pp_kind kind
153153+154154+ (* Maps *)
155155+156156+ let no_decoder meta ~kind = msgf meta "No decoder for %a" pp_kind kind
157157+ let no_encoder meta ~kind = msgf meta "No encoder for %a" pp_kind kind
158158+ let decode_todo meta ~kind_opt:k = msgf meta "TODO: decode%a" pp_kind_opt k
159159+ let encode_todo meta ~kind_opt:k = msgf meta "TODO: encode%a" pp_kind_opt k
160160+ let for' meta ~kind e = msgf meta "%a: %s" pp_kind kind e
161161+end
162162+163163+(* Types *)
164164+165165+module Repr = struct (* See the .mli for documentation *)
166166+ module String_map = Map.Make (String)
167167+ module Type = Jsont_base.Type
168168+169169+ type ('ret, 'f) dec_fun =
170170+ | Dec_fun : 'f -> ('ret, 'f) dec_fun
171171+ | Dec_app : ('ret, 'a -> 'b) dec_fun * 'a Type.Id.t -> ('ret, 'b) dec_fun
172172+173173+ type ('a, 'b) base_map =
174174+ { kind : string;
175175+ doc : string;
176176+ dec : Meta.t -> 'a -> 'b;
177177+ enc : 'b -> 'a;
178178+ enc_meta : 'b -> Meta.t; }
179179+180180+ type 'a t =
181181+ | Null : (unit, 'a) base_map -> 'a t
182182+ | Bool : (bool, 'a) base_map -> 'a t
183183+ | Number : (float, 'a) base_map -> 'a t
184184+ | String : (string, 'a) base_map -> 'a t
185185+ | Array : ('a, 'elt, 'builder) array_map -> 'a t
186186+ | Object : ('o, 'o) object_map -> 'o t
187187+ | Any : 'a any_map -> 'a t
188188+ | Map : ('a, 'b) map -> 'b t
189189+ | Rec : 'a t Lazy.t -> 'a t
190190+191191+ and ('array, 'elt, 'builder) array_map =
192192+ { kind : string;
193193+ doc : string;
194194+ elt : 'elt t;
195195+ dec_empty : unit -> 'builder;
196196+ dec_skip : int -> 'builder -> bool;
197197+ dec_add : int -> 'elt -> 'builder -> 'builder;
198198+ dec_finish : Meta.t -> int -> 'builder -> 'array;
199199+ enc : 'acc. ('acc -> int -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc;
200200+ enc_meta : 'array -> Meta.t; }
201201+202202+ and ('o, 'dec) object_map =
203203+ { kind : string;
204204+ doc : string;
205205+ dec : ('o, 'dec) dec_fun;
206206+ mem_decs : mem_dec String_map.t;
207207+ mem_encs : 'o mem_enc list;
208208+ enc_meta : 'o -> Meta.t;
209209+ shape : 'o object_shape; }
210210+211211+ and mem_dec = Mem_dec : ('o, 'a) mem_map -> mem_dec
212212+ and 'o mem_enc = Mem_enc : ('o, 'a) mem_map -> 'o mem_enc
213213+ and ('o, 'a) mem_map =
214214+ { name : string;
215215+ doc : string;
216216+ type' : 'a t;
217217+ id : 'a Type.Id.t;
218218+ dec_absent : 'a option;
219219+ enc : 'o -> 'a;
220220+ (* enc_name_meta : 'a -> Meta.t; See comment in .mli *)
221221+ enc_omit : 'a -> bool; }
222222+223223+ and 'o object_shape =
224224+ | Object_basic : ('o, 'mems, 'builder) unknown_mems -> 'o object_shape
225225+ | Object_cases :
226226+ ('o, 'mems, 'builder) unknown_mems option *
227227+ ('o, 'cases, 'tag) object_cases -> 'o object_shape
228228+229229+ and ('o, 'mems, 'builder) unknown_mems =
230230+ | Unknown_skip : ('o, unit, unit) unknown_mems
231231+ | Unknown_error : ('o, unit, unit) unknown_mems
232232+ | Unknown_keep :
233233+ ('mems, 'a, 'builder) mems_map * ('o -> 'mems) ->
234234+ ('o, 'mems, 'builder) unknown_mems
235235+236236+ and ('mems, 'a, 'builder) mems_map =
237237+ { kind : string;
238238+ doc : string;
239239+ mems_type : 'a t;
240240+ id : 'mems Type.Id.t;
241241+ dec_empty : unit -> 'builder;
242242+ dec_add : Meta.t -> string -> 'a -> 'builder -> 'builder;
243243+ dec_finish : Meta.t -> 'builder -> 'mems;
244244+ enc :
245245+ 'acc. (Meta.t -> string -> 'a -> 'acc -> 'acc) -> 'mems -> 'acc -> 'acc }
246246+247247+ and ('o, 'cases, 'tag) object_cases =
248248+ { tag : ('tag, 'tag) mem_map;
249249+ tag_compare : 'tag -> 'tag -> int;
250250+ tag_to_string : ('tag -> string) option;
251251+ id : 'cases Type.Id.t;
252252+ cases : ('cases, 'tag) case list;
253253+ enc : 'o -> 'cases;
254254+ enc_case : 'cases -> ('cases, 'tag) case_value; }
255255+256256+ and ('cases, 'case, 'tag) case_map =
257257+ { tag : 'tag;
258258+ object_map : ('case, 'case) object_map;
259259+ dec : 'case -> 'cases; }
260260+261261+ and ('cases, 'tag) case_value =
262262+ | Case_value :
263263+ ('cases, 'case, 'tag) case_map * 'case -> ('cases, 'tag) case_value
264264+265265+ and ('cases, 'tag) case =
266266+ | Case : ('cases, 'case, 'tag) case_map -> ('cases, 'tag) case
267267+268268+ and 'a any_map =
269269+ { kind : string;
270270+ doc : string;
271271+ dec_null : 'a t option;
272272+ dec_bool : 'a t option;
273273+ dec_number : 'a t option;
274274+ dec_string : 'a t option;
275275+ dec_array : 'a t option;
276276+ dec_object : 'a t option;
277277+ enc : 'a -> 'a t; }
278278+279279+ and ('a, 'b) map =
280280+ { kind : string;
281281+ doc : string;
282282+ dom : 'a t;
283283+ dec : 'a -> 'b;
284284+ enc : 'b -> 'a; }
285285+286286+ (* Convert *)
287287+288288+ let of_t = Fun.id
289289+ let unsafe_to_t = Fun.id
290290+291291+ (* Kinds and doc *)
292292+293293+ let base_map_with_doc ?kind ?doc (map : ('a, 'b) base_map) =
294294+ let kind = Option.value ~default:map.kind doc in
295295+ let doc = Option.value ~default:map.doc doc in
296296+ { map with kind; doc }
297297+298298+ let array_map_with_doc ?kind ?doc (map : ('a, 'b, 'c) array_map) =
299299+ let kind = Option.value ~default:map.kind doc in
300300+ let doc = Option.value ~default:map.doc doc in
301301+ { map with kind; doc }
302302+303303+ let object_map_with_doc ?kind ?doc (map : ('o, 'o) object_map) =
304304+ let kind = Option.value ~default:map.kind doc in
305305+ let doc = Option.value ~default:map.doc doc in
306306+ { map with kind; doc }
307307+308308+ let any_map_with_doc ?kind ?doc (map : 'a any_map) =
309309+ let kind = Option.value ~default:map.kind doc in
310310+ let doc = Option.value ~default:map.doc doc in
311311+ { map with kind; doc }
312312+313313+ let map_with_doc ?kind ?doc (map : ('a, 'b) map) =
314314+ let kind = Option.value ~default:map.kind doc in
315315+ let doc = Option.value ~default:map.doc doc in
316316+ { map with kind; doc }
317317+318318+ let rec with_doc ?kind ?doc = function
319319+ | Null map -> Null (base_map_with_doc ?kind ?doc map)
320320+ | Bool map -> Bool (base_map_with_doc ?kind ?doc map)
321321+ | Number map -> Number (base_map_with_doc ?kind ?doc map)
322322+ | String map -> String (base_map_with_doc ?kind ?doc map)
323323+ | Array map -> Array (array_map_with_doc ?kind ?doc map)
324324+ | Object map -> Object (object_map_with_doc ?kind ?doc map)
325325+ | Any map -> Any (any_map_with_doc ?kind ?doc map)
326326+ | Map map -> Map (map_with_doc ?kind ?doc map)
327327+ | Rec l -> with_doc ?kind ?doc (Lazy.force l)
328328+329329+ let object_map_kinded_sort (map : ('o, 'dec) object_map) =
330330+ Sort.kinded ~kind:map.kind Object
331331+332332+ let rec kinded_sort : type a. a t -> string = function
333333+ | Null map -> Sort.kinded ~kind:map.kind Null
334334+ | Bool map -> Sort.kinded ~kind:map.kind Bool
335335+ | Number map -> Sort.kinded ~kind:map.kind Number
336336+ | String map -> Sort.kinded ~kind:map.kind String
337337+ | Array map -> array_map_kinded_sort map
338338+ | Object map -> object_map_kinded_sort map
339339+ | Any map -> if map.kind = "" then any_map_kinded_sort map else map.kind
340340+ | Map map -> if map.kind = "" then kinded_sort map.dom else map.kind
341341+ | Rec l -> kinded_sort (Lazy.force l)
342342+343343+ and array_map_kinded_sort : type a e b. (a, e, b) array_map -> string =
344344+ fun map ->
345345+ if map.kind <> "" then Sort.kinded ~kind:map.kind Array else
346346+ let elt = kinded_sort map.elt in
347347+ String.concat "" ["array<"; elt; ">"]
348348+349349+ and any_map_kinded_sort : type a. a any_map -> string = fun map ->
350350+ let add_case ks sort = function
351351+ | None -> ks
352352+ | Some k ->
353353+ (if map.kind <> "" then kinded_sort k else
354354+ Sort.kinded ~kind:map.kind sort)
355355+ :: ks
356356+ in
357357+ let ks = add_case [] Object map.dec_object in
358358+ let ks = add_case ks Array map.dec_array in
359359+ let ks = add_case ks String map.dec_string in
360360+ let ks = add_case ks Number map.dec_number in
361361+ let ks = add_case ks Bool map.dec_bool in
362362+ let ks = add_case ks Null map.dec_null in
363363+ "one of " ^ String.concat ", " ks
364364+365365+ let rec kind : type a. a t -> string = function
366366+ | Null map -> Sort.or_kind ~kind:map.kind Null
367367+ | Bool map -> Sort.or_kind ~kind:map.kind Bool
368368+ | Number map -> Sort.or_kind ~kind:map.kind Number
369369+ | String map -> Sort.or_kind ~kind:map.kind String
370370+ | Array map -> Sort.or_kind ~kind:map.kind Array
371371+ | Object map -> Sort.or_kind ~kind:map.kind Object
372372+ | Any map -> if map.kind <> "" then map.kind else "any"
373373+ | Map map -> if map.kind <> "" then map.kind else kind map.dom
374374+ | Rec l -> kind (Lazy.force l)
375375+376376+ let rec doc : type a. a t -> string = function
377377+ | Null map -> map.doc | Bool map -> map.doc | Number map -> map.doc
378378+ | String map -> map.doc | Array map -> map.doc | Object map -> map.doc
379379+ | Any map -> map.doc | Map map -> map.doc | Rec l -> doc (Lazy.force l)
380380+381381+ (* Errors *)
382382+383383+ let pp_code = Fmt.code
384384+ let pp_kind = pp_kind
385385+386386+ let error_push_object meta map name e =
387387+ Error.push_object ((object_map_kinded_sort map), meta) name e
388388+389389+ let error_push_array meta map i e =
390390+ Error.push_array ((array_map_kinded_sort map), meta) i e
391391+392392+ let type_error meta t ~fnd =
393393+ Error.kinded_sort meta ~exp:(kinded_sort t) ~fnd
394394+395395+ let missing_mems_error meta (object_map : ('o, 'o) object_map) ~exp ~fnd =
396396+ let kinded_sort = object_map_kinded_sort object_map in
397397+ let exp =
398398+ let add n (Mem_dec m) acc = match m.dec_absent with
399399+ | None -> n :: acc | Some _ -> acc
400400+ in
401401+ List.rev (String_map.fold add exp [])
402402+ in
403403+ Error.missing_mems meta ~kinded_sort ~exp ~fnd
404404+405405+ let unexpected_mems_error meta (object_map : ('o, 'o) object_map) ~fnd =
406406+ let kinded_sort = object_map_kinded_sort object_map in
407407+ let exp = List.map (fun (Mem_enc m) -> m.name) object_map.mem_encs in
408408+ Error.unexpected_mems meta ~kinded_sort ~exp ~fnd
409409+410410+ let unexpected_case_tag_error meta object_map object_cases tag =
411411+ let kinded_sort = object_map_kinded_sort object_map in
412412+ let case_to_string (Case c) = match object_cases.tag_to_string with
413413+ | None -> None | Some str -> Some (str c.tag)
414414+ in
415415+ let exp = List.filter_map case_to_string object_cases.cases in
416416+ let fnd = match object_cases.tag_to_string with
417417+ | None -> "<tag>" (* XXX not good *) | Some str -> str tag
418418+ in
419419+ let mem_name = object_cases.tag.name in
420420+ Error.unexpected_case_tag meta ~kinded_sort ~mem_name ~exp ~fnd
421421+422422+ (* Processor toolbox *)
423423+424424+ let object_meta_arg : Meta.t Type.Id.t = Type.Id.make ()
425425+426426+ module Dict = struct
427427+ module M = Map.Make (Int)
428428+ type binding = B : 'a Type.Id.t * 'a -> binding
429429+ type t = binding M.t
430430+ let empty = M.empty
431431+ let mem k m = M.mem (Type.Id.uid k) m
432432+ let add k v m = M.add (Type.Id.uid k) (B (k, v)) m
433433+ let remove k m = M.remove (Type.Id.uid k) m
434434+ let find : type a. a Type.Id.t -> t -> a option =
435435+ fun k m -> match M.find_opt (Type.Id.uid k) m with
436436+ | None -> None
437437+ | Some B (k', v) ->
438438+ match Type.Id.provably_equal k k' with
439439+ | Some Type.Equal -> Some v | None -> assert false
440440+ end
441441+442442+ let rec apply_dict : type ret f. (ret, f) dec_fun -> Dict.t -> f =
443443+ fun dec dict -> match dec with
444444+ | Dec_fun f -> f
445445+ | Dec_app (f, arg) -> (apply_dict f dict) (Option.get (Dict.find arg dict))
446446+447447+ type unknown_mems_option =
448448+ | Unknown_mems :
449449+ ('o, 'mems, 'builder) unknown_mems option -> unknown_mems_option
450450+451451+ let override_unknown_mems ~by umems dict = match by with
452452+ | Unknown_mems None -> umems, dict
453453+ | Unknown_mems _ as by ->
454454+ match umems with
455455+ | Unknown_mems (Some (Unknown_keep (umap, _))) ->
456456+ (* A decoding function still expect [umap.id] argument in
457457+ an Dec_app, we simply stub it with the empty map. *)
458458+ let empty = umap.dec_finish Meta.none (umap.dec_empty ()) in
459459+ let dict = Dict.add umap.id empty dict in
460460+ by, dict
461461+ | _ -> by, dict
462462+463463+ let finish_object_decode : type o p m mems builder.
464464+ (o, o) object_map -> Meta.t -> (p, mems, builder) unknown_mems -> builder ->
465465+ mem_dec String_map.t -> Dict.t -> Dict.t
466466+ =
467467+ fun map meta umems umap mem_decs dict ->
468468+ let dict = Dict.add object_meta_arg meta dict in
469469+ let dict = match umems with
470470+ | Unknown_skip | Unknown_error -> dict
471471+ | Unknown_keep (map, _) -> Dict.add map.id (map.dec_finish meta umap) dict
472472+ in
473473+ let add_default _ (Mem_dec mem_map) dict = match mem_map.dec_absent with
474474+ | Some v -> Dict.add mem_map.id v dict
475475+ | None -> raise Exit
476476+ in
477477+ (try String_map.fold add_default mem_decs dict with
478478+ | Exit ->
479479+ let no_default _ (Mem_dec mm) = Option.is_none mm.dec_absent in
480480+ let exp = String_map.filter no_default mem_decs in
481481+ missing_mems_error meta map ~exp ~fnd:[])
482482+end
483483+484484+(* Types *)
485485+486486+type 'a t = 'a Repr.t
487487+let kinded_sort = Repr.kinded_sort
488488+let kind = Repr.kind
489489+let doc = Repr.doc
490490+let with_doc = Repr.with_doc
491491+492492+(* Base types *)
493493+494494+let enc_meta_none _v = Meta.none
495495+496496+module Base = struct
497497+ type ('a, 'b) map = ('a, 'b) Repr.base_map
498498+499499+ let base_map_sort = "base map"
500500+501501+ let map ?(kind = "") ?(doc = "") ?dec ?enc ?(enc_meta = enc_meta_none) () =
502502+ let dec = match dec with
503503+ | Some dec -> dec
504504+ | None ->
505505+ let kind = Sort.kinded' ~kind base_map_sort in
506506+ fun meta _v -> Error.no_decoder meta ~kind
507507+ in
508508+ let enc = match enc with
509509+ | Some enc -> enc
510510+ | None ->
511511+ let kind = Sort.kinded' ~kind base_map_sort in
512512+ fun _v -> Error.no_encoder Meta.none ~kind
513513+ in
514514+ { Repr.kind; doc; dec; enc; enc_meta }
515515+516516+ let id =
517517+ let dec _meta v = v and enc = Fun.id in
518518+ { Repr.kind = ""; doc = ""; dec; enc; enc_meta = enc_meta_none }
519519+520520+ let ignore =
521521+ let kind = "ignore" in
522522+ let dec _meta _v = () in
523523+ let enc _v =
524524+ let kind = Sort.kinded' ~kind base_map_sort in
525525+ Error.no_encoder Meta.none ~kind
526526+ in
527527+ { Repr.kind; doc = ""; dec; enc; enc_meta = enc_meta_none }
528528+529529+ let null map = Repr.Null map
530530+ let bool map = Repr.Bool map
531531+ let number map = Repr.Number map
532532+ let string map = Repr.String map
533533+534534+ let dec dec = fun _meta v -> dec v
535535+ let dec_result ?(kind = "") dec =
536536+ let kind = Sort.kinded' ~kind base_map_sort in
537537+ fun meta v -> match dec v with
538538+ | Ok v -> v | Error e -> Error.for' meta ~kind e
539539+540540+ let dec_failure ?(kind = "") dec =
541541+ let kind = Sort.kinded' ~kind base_map_sort in
542542+ fun meta v -> try dec v with Failure e -> Error.for' meta ~kind e
543543+544544+ let enc = Fun.id
545545+ let enc_result ?(kind = "") enc =
546546+ let kind = Sort.kinded' ~kind base_map_sort in
547547+ fun v -> match enc v with
548548+ | Ok v -> v | Error e -> Error.for' Meta.none ~kind e
549549+550550+ let enc_failure ?(kind = "") enc =
551551+ let kind = Sort.kinded' ~kind base_map_sort in
552552+ fun v -> try enc v with Failure e -> Error.for' Meta.none ~kind e
553553+end
554554+555555+(* Any *)
556556+557557+let any
558558+ ?(kind = "") ?(doc = "") ?dec_null ?dec_bool ?dec_number ?dec_string
559559+ ?dec_array ?dec_object ?enc ()
560560+ =
561561+ let enc = match enc with
562562+ | Some enc -> enc
563563+ | None ->
564564+ let kind = Sort.kinded' ~kind "any" in
565565+ fun _v -> Error.no_encoder Meta.none ~kind
566566+ in
567567+ Repr.Any { kind; doc; dec_null; dec_bool; dec_number; dec_string; dec_array;
568568+ dec_object; enc }
569569+570570+(* Maps and recursion *)
571571+572572+let map ?(kind = "") ?(doc = "") ?dec ?enc dom =
573573+ let map_sort = "map" in
574574+ let dec = match dec with
575575+ | Some dec -> dec
576576+ | None ->
577577+ let kind = Sort.kinded' ~kind map_sort in
578578+ fun _v -> Error.no_decoder Meta.none ~kind
579579+ in
580580+ let enc = match enc with
581581+ | Some enc -> enc
582582+ | None ->
583583+ let kind = Sort.kinded' ~kind map_sort in
584584+ fun _v -> Error.no_encoder Meta.none ~kind
585585+ in
586586+ Repr.Map { kind; doc; dom; dec; enc }
587587+588588+let iter ?(kind = "") ?(doc = "") ?dec ?enc dom =
589589+ let dec = match dec with
590590+ | None -> Fun.id | Some dec -> fun v -> dec v; v
591591+ in
592592+ let enc = match enc with
593593+ | None -> Fun.id | Some enc -> fun v -> enc v; v
594594+ in
595595+ Repr.Map { kind; doc; dom; dec; enc }
596596+597597+let rec' t = Repr.Rec t
598598+599599+(* Nulls and options *)
600600+601601+let null ?kind ?doc v =
602602+ let dec _meta () = v and enc _meta = () in
603603+ Repr.Null (Base.map ?doc ?kind ~dec ~enc ())
604604+605605+let none =
606606+ let none = (* Can't use [Base.map] because of the value restriction. *)
607607+ let dec _meta _v = None and enc _ = () in
608608+ { Repr.kind = ""; doc = ""; dec; enc; enc_meta = enc_meta_none }
609609+ in
610610+ Repr.Null none
611611+612612+let some t = map ~dec:Option.some ~enc:Option.get t
613613+614614+let option ?kind ?doc t =
615615+ let some = some t in
616616+ let enc = function None -> none | Some _ -> some in
617617+ match t with
618618+ | Null _ -> any ?doc ?kind ~dec_null:none ~enc ()
619619+ | Bool _ -> any ?doc ?kind ~dec_null:none ~dec_bool:some ~enc ()
620620+ | Number _ -> any ?doc ?kind ~dec_null:none ~dec_number:some ~enc ()
621621+ | String _ -> any ?doc ?kind ~dec_null:none ~dec_string:some ~enc ()
622622+ | Array _ -> any ?doc ?kind ~dec_null:none ~dec_array:some ~enc ()
623623+ | Object _ -> any ?doc ?kind ~dec_null:none ~dec_object:some ~enc ()
624624+ | (Any _ | Map _ | Rec _) ->
625625+ any ?doc ?kind ~dec_null:none ~dec_bool:some ~dec_number:some
626626+ ~dec_string:some ~dec_array:some ~dec_object:some ~enc ()
627627+628628+(* Booleans *)
629629+630630+let bool = Repr.Bool Base.id
631631+632632+(* Numbers *)
633633+634634+let[@inline] check_finite_number meta ~kind v =
635635+ if Float.is_finite v then () else
636636+ Error.kinded_sort meta ~exp:(Sort.kinded ~kind Number) ~fnd:Sort.Null
637637+638638+let number = Repr.Number Base.id
639639+640640+let any_float =
641641+ let kind = "float" in
642642+ let finite = number in
643643+ let non_finite =
644644+ let dec m v = match Float.of_string_opt v with
645645+ | Some v -> v | None -> Error.parse_string_number m ~kind v
646646+ in
647647+ Base.string (Base.map ~kind ~dec ~enc:Float.to_string ())
648648+ in
649649+ let enc v = if Float.is_finite v then finite else non_finite in
650650+ any ~kind ~dec_null:finite ~dec_number:finite ~dec_string:non_finite ~enc ()
651651+652652+let float_as_hex_string =
653653+ let kind = "float" in
654654+ let dec meta v = match Float.of_string_opt v with
655655+ | Some v -> v | None -> Error.parse_string_number meta ~kind v
656656+ in
657657+ let enc v = Printf.sprintf "%h" v in
658658+ Base.string (Base.map ~kind ~dec ~enc ())
659659+660660+let uint8 =
661661+ let kind = "uint8" in
662662+ let dec meta v =
663663+ check_finite_number meta ~kind v;
664664+ if Jsont_base.Number.in_exact_uint8_range v then Int.of_float v else
665665+ Error.number_range meta ~kind v
666666+ in
667667+ let enc v =
668668+ if Jsont_base.Number.int_is_uint8 v then Int.to_float v else
669669+ Error.integer_range Meta.none ~kind v
670670+ in
671671+ Base.number (Base.map ~kind ~dec ~enc ())
672672+673673+let uint16 =
674674+ let kind = "uint16" in
675675+ let dec meta v =
676676+ check_finite_number meta ~kind v;
677677+ if Jsont_base.Number.in_exact_uint16_range v then Int.of_float v else
678678+ Error.number_range meta ~kind v
679679+ in
680680+ let enc v =
681681+ if Jsont_base.Number.int_is_uint16 v then Int.to_float v else
682682+ Error.integer_range Meta.none ~kind v
683683+ in
684684+ Base.number (Base.map ~kind ~dec ~enc ())
685685+686686+let int8 =
687687+ let kind = "int8" in
688688+ let dec meta v =
689689+ check_finite_number meta ~kind v;
690690+ if Jsont_base.Number.in_exact_int8_range v then Int.of_float v else
691691+ Error.number_range meta ~kind v
692692+ in
693693+ let enc v =
694694+ if Jsont_base.Number.int_is_int8 v then Int.to_float v else
695695+ Error.integer_range Meta.none ~kind v
696696+ in
697697+ Base.number (Base.map ~kind ~dec ~enc ())
698698+699699+let int16 =
700700+ let kind = "int16" in
701701+ let dec meta v =
702702+ check_finite_number meta ~kind v;
703703+ if Jsont_base.Number.in_exact_int16_range v then Int.of_float v else
704704+ Error.number_range meta ~kind v
705705+ in
706706+ let enc v =
707707+ if Jsont_base.Number.int_is_int16 v then Int.to_float v else
708708+ Error.integer_range Meta.none ~kind v
709709+ in
710710+ Base.number (Base.map ~kind ~dec ~enc ())
711711+712712+let int32 =
713713+ let kind = "int32" in
714714+ let dec meta v =
715715+ check_finite_number meta ~kind v;
716716+ if Jsont_base.Number.in_exact_int32_range v then Int32.of_float v else
717717+ Error.number_range meta ~kind v
718718+ in
719719+ let enc = Int32.to_float (* Everything always fits *) in
720720+ Base.number (Base.map ~kind ~dec ~enc ())
721721+722722+let int64_as_string =
723723+ let kind = "int64" in
724724+ let dec meta v = match Int64.of_string_opt v with
725725+ | Some v -> v | None -> Error.parse_string_number meta ~kind v
726726+ in
727727+ Base.string (Base.map ~kind ~dec ~enc:Int64.to_string ())
728728+729729+let int64_number =
730730+ (* Usage by [int64] entails there's no need to test for nan or check
731731+ range on encoding. *)
732732+ let kind = "int64" in
733733+ let dec meta v =
734734+ if Jsont_base.Number.in_exact_int64_range v then Int64.of_float v else
735735+ Error.number_range meta ~kind v
736736+ in
737737+ Base.number (Base.map ~kind ~dec ~enc:Int64.to_float ())
738738+739739+let int64 =
740740+ let dec_number = int64_number and dec_string = int64_as_string in
741741+ let enc v =
742742+ if Jsont_base.Number.can_store_exact_int64 v then int64_number else
743743+ int64_as_string
744744+ in
745745+ any ~kind:"int64" ~dec_number ~dec_string ~enc ()
746746+747747+let int_as_string =
748748+ let kind = "OCaml int" in
749749+ let dec meta v = match int_of_string_opt v with
750750+ | Some v -> v | None -> Error.parse_string_number meta ~kind v
751751+ in
752752+ Base.string (Base.map ~kind ~dec ~enc:Int.to_string ())
753753+754754+let int_number =
755755+ (* Usage by [int] entails there's no need to test for nan or check range on
756756+ encoding. *)
757757+ let kind = "OCaml int" in
758758+ let dec meta v =
759759+ if Jsont_base.Number.in_exact_int_range v then Int.of_float v else
760760+ Error.number_range meta ~kind v
761761+ in
762762+ Base.number (Base.map ~kind ~dec ~enc:Int.to_float ())
763763+764764+let int =
765765+ let enc v =
766766+ if Jsont_base.Number.can_store_exact_int v then int_number else
767767+ int_as_string
768768+ in
769769+ let dec_number = int_number and dec_string = int_as_string in
770770+ any ~kind:"OCaml int" ~dec_number ~dec_string ~enc ()
771771+772772+(* String and enums *)
773773+774774+let string = Repr.String Base.id
775775+776776+let of_of_string ?kind ?doc ?enc of_string =
777777+ let dec = Base.dec_result ?kind of_string in
778778+ let enc = match enc with None -> None | Some enc -> Some (Base.enc enc) in
779779+ Base.string (Base.map ?kind ?doc ?enc ~dec ())
780780+781781+let enum (type a) ?(cmp = Stdlib.compare) ?(kind = "") ?doc assoc =
782782+ let kind = Sort.kinded' ~kind "enum" in
783783+ let dec_map =
784784+ let add m (k, v) = Repr.String_map.add k v m in
785785+ let m = List.fold_left add Repr.String_map.empty assoc in
786786+ fun k -> Repr.String_map.find_opt k m
787787+ in
788788+ let enc_map =
789789+ let module M = Map.Make (struct type t = a let compare = cmp end) in
790790+ let add m (k, v) = M.add v k m in
791791+ let m = List.fold_left add M.empty assoc in
792792+ fun v -> M.find_opt v m
793793+ in
794794+ let dec meta s = match dec_map s with
795795+ | Some v -> v
796796+ | None ->
797797+ let kind = Sort.kinded ~kind String in
798798+ let pp_kind ppf () = Fmt.pf ppf "%a value" Repr.pp_kind kind in
799799+ Error.msgf meta "%a" (Fmt.out_of_dom ~pp_kind ()) (s, List.map fst assoc)
800800+ in
801801+ let enc v = match enc_map v with
802802+ | Some s -> s
803803+ | None ->
804804+ Error.msgf Meta.none "Encode %a: unknown enum value" Repr.pp_kind kind
805805+ in
806806+ Base.string (Base.map ~kind ?doc ~dec ~enc ())
807807+808808+let binary_string =
809809+ let kind = "hex" in
810810+ let kind' = Sort.kinded ~kind String in
811811+ let dec = Base.dec_result ~kind:kind' Jsont_base.binary_string_of_hex in
812812+ let enc = Base.enc Jsont_base.binary_string_to_hex in
813813+ Base.string (Base.map ~kind ~dec ~enc ())
814814+815815+(* Arrays and tuples *)
816816+817817+module Array = struct
818818+ type ('array, 'elt, 'builder) map = ('array, 'elt, 'builder) Repr.array_map
819819+ type ('array, 'elt) enc =
820820+ { enc : 'acc. ('acc -> int -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc }
821821+822822+ let array_kind kind = Sort.kinded ~kind Sort.Array
823823+ let default_skip _i _builder = false
824824+ let map
825825+ ?(kind = "") ?(doc = "") ?dec_empty ?dec_skip ?dec_add ?dec_finish
826826+ ?enc ?(enc_meta = enc_meta_none) elt
827827+ =
828828+ let dec_empty = match dec_empty with
829829+ | Some dec_empty -> dec_empty
830830+ | None -> fun () -> Error.no_decoder Meta.none ~kind:(array_kind kind)
831831+ in
832832+ let dec_skip = Option.value ~default:default_skip dec_skip in
833833+ let dec_add = match dec_add with
834834+ | Some dec_add -> dec_add
835835+ | None -> fun _ _ _ -> Error.no_decoder Meta.none ~kind:(array_kind kind)
836836+ in
837837+ let dec_finish = match dec_finish with
838838+ | Some dec_finish -> dec_finish
839839+ | None -> fun _ _ _ -> Error.no_decoder Meta.none ~kind:(array_kind kind)
840840+ in
841841+ let enc = match enc with
842842+ | Some { enc } -> enc
843843+ | None -> fun _ _ _ -> Error.no_encoder Meta.none ~kind:(array_kind kind)
844844+ in
845845+ { Repr.kind; doc; elt; dec_empty; dec_add; dec_skip; dec_finish; enc;
846846+ enc_meta; }
847847+848848+ let list_enc f acc l =
849849+ let rec loop f acc i = function
850850+ | [] -> acc | v :: l -> loop f (f acc i v) (i + 1) l
851851+ in
852852+ loop f acc 0 l
853853+854854+ let list_map ?kind ?doc ?dec_skip elt =
855855+ let dec_empty () = [] in
856856+ let dec_add _i v l = v :: l in
857857+ let dec_finish _meta _len l = List.rev l in
858858+ let enc = { enc = list_enc } in
859859+ map ?kind ?doc ~dec_empty ?dec_skip ~dec_add ~dec_finish ~enc elt
860860+861861+ type 'a array_builder = 'a Jsont_base.Rarray.t
862862+863863+ let array_enc f acc a =
864864+ let acc = ref acc in
865865+ for i = 0 to Array.length a - 1
866866+ do acc := f !acc i (Array.unsafe_get a i) done;
867867+ !acc
868868+869869+ let array_map ?kind ?doc ?dec_skip elt =
870870+ let dec_empty () = Jsont_base.Rarray.empty () in
871871+ let dec_add _i v a = Jsont_base.Rarray.add_last v a in
872872+ let dec_finish _meta _len a = Jsont_base.Rarray.to_array a in
873873+ let enc = { enc = array_enc } in
874874+ map ?kind ?doc ~dec_empty ?dec_skip ~dec_add ~dec_finish ~enc elt
875875+876876+ type ('a, 'b, 'c) bigarray_builder = ('a, 'b, 'c) Jsont_base.Rbigarray1.t
877877+878878+ let bigarray_map ?kind ?doc ?dec_skip k l elt =
879879+ let dec_empty _meta = Jsont_base.Rbigarray1.empty k l in
880880+ let dec_add _i v a = Jsont_base.Rbigarray1.add_last v a in
881881+ let dec_finish _meta _len a = Jsont_base.Rbigarray1.to_bigarray a in
882882+ let enc f acc a =
883883+ let acc = ref acc in
884884+ for i = 0 to Bigarray.Array1.dim a - 1
885885+ do acc := f !acc i (Bigarray.Array1.unsafe_get a i) done;
886886+ !acc
887887+ in
888888+ let enc = { enc } in
889889+ map ?kind ?doc ~dec_empty ?dec_skip ~dec_add ~dec_finish ~enc elt
890890+891891+ let array map = Repr.Array map
892892+893893+ let stub_elt =
894894+ Repr.Map { kind = ""; doc = ""; dom = Base.(null id);
895895+ enc = (fun _ -> assert false);
896896+ dec = (fun _ -> assert false); }
897897+898898+ let ignore =
899899+ let kind = "ignore" in
900900+ let kind' = Sort.kinded ~kind Array in
901901+ let dec_empty () = () and dec_add _i _v () = () in
902902+ let dec_skip _i () = true and dec_finish _meta _len () = () in
903903+ let enc = { enc = fun _ _ () -> Error.no_encoder Meta.none ~kind:kind' } in
904904+ array (map ~kind ~dec_empty ~dec_skip ~dec_add ~dec_finish ~enc stub_elt)
905905+906906+ let zero =
907907+ let dec_empty () = () and dec_add _i _v () = () in
908908+ let dec_skip _i () = true and dec_finish _meta _len () = () in
909909+ let enc = { enc = fun _ acc () -> acc } in
910910+ let kind = "zero" in
911911+ array (map ~kind ~dec_empty ~dec_skip ~dec_add ~dec_finish ~enc stub_elt)
912912+end
913913+914914+let list ?kind ?doc t = Repr.Array (Array.list_map ?kind ?doc t)
915915+let array ?kind ?doc t = Repr.Array (Array.array_map ?kind ?doc t)
916916+let array_as_string_map ?kind ?doc ~key t =
917917+ let dec_empty () = Repr.String_map.empty in
918918+ let dec_add _i elt acc = Repr.String_map.add (key elt) elt acc in
919919+ let dec_finish _meta _len acc = acc in
920920+ let enc f acc m =
921921+ let i = ref (-1) in
922922+ Repr.String_map.fold (fun _ elt acc -> incr i; f acc !i elt) m acc
923923+ in
924924+ let enc = Array.{enc} in
925925+ let map = Array.map ?kind ?doc ~dec_empty ~dec_add ~dec_finish ~enc t in
926926+ Repr.Array map
927927+928928+let bigarray ?kind ?doc k t =
929929+ Repr.Array (Array.bigarray_map ?kind ?doc k Bigarray.c_layout t)
930930+931931+let tuple_no_decoder ~kind meta =
932932+ Error.no_decoder meta ~kind:(Sort.kinded' ~kind "tuple")
933933+934934+let tuple_no_encoder ~kind =
935935+ Error.no_encoder Meta.none ~kind:(Sort.kinded' ~kind "tuple")
936936+937937+let error_tuple_size meta kind ~exp fnd =
938938+ Error.msgf meta "Expected %a elements in %a but found %a"
939939+ pp_int exp pp_kind (Sort.kinded' ~kind "tuple") pp_int fnd
940940+941941+let t2 ?(kind = "") ?doc ?dec ?enc t =
942942+ let size = 2 in
943943+ let dec = match dec with
944944+ | None -> fun meta _v0 _v1 -> tuple_no_decoder ~kind meta
945945+ | Some dec -> fun _meta v0 v1 -> dec v0 v1
946946+ in
947947+ let dec_empty () = [] in
948948+ let dec_add _i v acc = v :: acc in
949949+ let dec_finish meta _len = function
950950+ | [v1; v0] -> dec meta v0 v1
951951+ | l -> error_tuple_size meta kind ~exp:size (List.length l)
952952+ in
953953+ let enc = match enc with
954954+ | None -> fun _f _acc _v -> tuple_no_encoder ~kind
955955+ | Some enc -> fun f acc v -> f (f acc 0 (enc v 0)) 1 (enc v 1)
956956+ in
957957+ let enc = { Array.enc } in
958958+ Repr.Array (Array.map ~kind ?doc ~dec_empty ~dec_add ~dec_finish ~enc t)
959959+960960+let t3 ?(kind = "") ?doc ?dec ?enc t =
961961+ let size = 3 in
962962+ let dec = match dec with
963963+ | None -> fun meta _v0 _v1 _v2 -> tuple_no_decoder ~kind meta
964964+ | Some dec -> fun _meta v0 v1 v2 -> dec v0 v1 v2
965965+ in
966966+ let dec_empty () = [] in
967967+ let dec_add _i v acc = v :: acc in
968968+ let dec_finish meta _len = function
969969+ | [v2; v1; v0] -> dec meta v0 v1 v2
970970+ | l -> error_tuple_size meta kind ~exp:size (List.length l)
971971+ in
972972+ let enc = match enc with
973973+ | None -> fun _f _acc _v -> tuple_no_encoder ~kind
974974+ | Some enc ->
975975+ fun f acc v -> f (f (f acc 0 (enc v 0)) 1 (enc v 1)) 2 (enc v 2)
976976+ in
977977+ let enc = { Array.enc } in
978978+ Repr.Array (Array.map ~kind ?doc ~dec_empty ~dec_add ~dec_finish ~enc t)
979979+980980+let t4 ?(kind = "") ?doc ?dec ?enc t =
981981+ let size = 4 in
982982+ let dec = match dec with
983983+ | None -> fun meta _v0 _v1 _v2 _v3 -> tuple_no_decoder ~kind meta
984984+ | Some dec -> fun _meta v0 v1 v2 v3 -> dec v0 v1 v2 v3
985985+ in
986986+ let dec_empty () = [] in
987987+ let dec_add _i v acc = v :: acc in
988988+ let dec_finish meta _len = function
989989+ | [v3; v2; v1; v0] -> dec meta v0 v1 v2 v3
990990+ | l -> error_tuple_size meta kind ~exp:size (List.length l)
991991+ in
992992+ let enc = match enc with
993993+ | None -> fun _f _acc _v -> tuple_no_encoder ~kind
994994+ | Some enc ->
995995+ fun f acc v ->
996996+ f (f (f (f acc 0 (enc v 0)) 1 (enc v 1)) 2 (enc v 2)) 3 (enc v 3)
997997+ in
998998+ let enc = { Array.enc } in
999999+ Repr.Array (Array.map ~kind ?doc ~dec_empty ~dec_add ~dec_finish ~enc t)
10001000+10011001+let tn ?(kind = "") ?doc ~n elt =
10021002+ let dec_empty () = Jsont_base.Rarray.empty () in
10031003+ let dec_add _i v a = Jsont_base.Rarray.add_last v a in
10041004+ let dec_finish meta _len a =
10051005+ let len = Jsont_base.Rarray.length a in
10061006+ if len <> n then error_tuple_size meta kind ~exp:n len else
10071007+ Jsont_base.Rarray.to_array a
10081008+ in
10091009+ let enc = { Array.enc = Array.array_enc } in
10101010+ Repr.Array (Array.map ~kind ?doc ~dec_empty ~dec_add ~dec_finish ~enc elt)
10111011+10121012+(* Objects *)
10131013+10141014+module Object = struct
10151015+ open Repr
10161016+10171017+ (* Maps *)
10181018+10191019+ type ('o, 'dec) map = ('o, 'dec) object_map
10201020+10211021+ let default_shape = Object_basic Unknown_skip
10221022+10231023+ let _map ?(kind = "") ?(doc = "") ?(enc_meta = enc_meta_none) dec =
10241024+ { kind; doc; dec; mem_decs = String_map.empty; mem_encs = [];
10251025+ enc_meta; shape = default_shape }
10261026+10271027+ let map ?kind ?doc dec = _map ?kind ?doc (Dec_fun dec)
10281028+ let map' ?kind ?doc ?enc_meta dec =
10291029+ _map ?kind ?doc ?enc_meta (Dec_app (Dec_fun dec, object_meta_arg))
10301030+10311031+ let enc_only ?(kind = "") ?doc ?enc_meta () =
10321032+ let dec meta = Error.no_decoder meta ~kind:(Sort.kinded ~kind Object) in
10331033+ map' ~kind ?doc ?enc_meta dec
10341034+10351035+ let check_name_unicity m =
10361036+ let add n kind = function
10371037+ | None -> Some kind
10381038+ | Some kind' ->
10391039+ let ks k = Sort.or_kind ~kind Object in
10401040+ let k0 = ks kind and k1 = ks kind' in
10411041+ invalid_arg @@
10421042+ if String.equal k0 k1
10431043+ then Fmt.str "member %s defined twice in %s" n k0
10441044+ else Fmt.str "member %s defined both in %s and %s" n k0 k1
10451045+ in
10461046+ let rec loop :
10471047+ type o dec. string String_map.t -> (o, dec) object_map -> unit
10481048+ =
10491049+ fun names m ->
10501050+ let add_name names n = String_map.update n (add n m.kind) names in
10511051+ let add_mem_enc names (Mem_enc m) = add_name names m.name in
10521052+ let names = List.fold_left add_mem_enc names m.mem_encs in
10531053+ match m.shape with
10541054+ | Object_basic _ -> ()
10551055+ | Object_cases (u, cases) ->
10561056+ let names = add_name names cases.tag.name in
10571057+ let check_case (Case c) = loop names c.object_map in
10581058+ List.iter check_case cases.cases
10591059+ in
10601060+ loop String_map.empty m
10611061+10621062+ let finish mems =
10631063+ let () = check_name_unicity mems in
10641064+ Object { mems with mem_encs = List.rev mems.mem_encs }
10651065+10661066+ let get_object_map = function
10671067+ | Object map -> map | _ -> invalid_arg "Not an object"
10681068+10691069+ (* Members *)
10701070+10711071+ module Mem = struct
10721072+ type ('o, 'a) map = ('o, 'a) Repr.mem_map
10731073+10741074+ let no_enc name = fun _v ->
10751075+ Error.msgf Meta.none "No encoder for member %a" pp_code name
10761076+10771077+ let map ?(doc = "") ?dec_absent ?enc ?enc_omit name type' =
10781078+ let id = Type.Id.make () in
10791079+ let enc = match enc with None -> no_enc name | Some enc -> enc in
10801080+ let enc_omit = match enc_omit with
10811081+ | None -> Fun.const false | Some omit -> omit
10821082+ in
10831083+ { name; doc; type'; id; dec_absent; enc; enc_omit }
10841084+10851085+ let app object_map mm =
10861086+ let mem_decs = String_map.add mm.name (Mem_dec mm) object_map.mem_decs in
10871087+ let mem_encs = Mem_enc mm :: object_map.mem_encs in
10881088+ let dec = Dec_app (object_map.dec, mm.id) in
10891089+ { object_map with dec; mem_decs; mem_encs }
10901090+ end
10911091+10921092+ let mem ?(doc = "") ?dec_absent ?enc ?enc_omit name type' map =
10931093+ let mmap = Mem.map ~doc ?dec_absent ?enc ?enc_omit name type' in
10941094+ let mem_decs = String_map.add name (Mem_dec mmap) map.mem_decs in
10951095+ let mem_encs = Mem_enc mmap :: map.mem_encs in
10961096+ let dec = Dec_app (map.dec, mmap.id) in
10971097+ { map with dec; mem_decs; mem_encs }
10981098+10991099+ let opt_mem ?doc ?enc:e name dom map =
11001100+ let dec = Option.some and enc = Option.get in
11011101+ let some = Map { kind = ""; doc = ""; dom; dec; enc} in
11021102+ mem ?doc ~dec_absent:None ?enc:e ~enc_omit:Option.is_none name some map
11031103+11041104+ (* Case objects *)
11051105+11061106+ module Case = struct
11071107+ type ('cases, 'case, 'tag) map = ('cases, 'case, 'tag) case_map
11081108+ type ('cases, 'tag) t = ('cases, 'tag) case
11091109+ type ('cases, 'tag) value = ('cases, 'tag) case_value
11101110+11111111+ let no_dec _ = Error.msgf Meta.none "No decoder for case"
11121112+ let map ?(dec = no_dec) tag obj =
11131113+ { tag; object_map = get_object_map obj; dec; }
11141114+11151115+ let map_tag c = c.tag
11161116+ let make c = Case c
11171117+ let tag (Case c) = map_tag c
11181118+ let value c v = Case_value (c, v)
11191119+ end
11201120+11211121+ let check_case_mem map cases ~dec_absent ~tag_compare ~tag_to_string =
11221122+ match map.shape with
11231123+ | Object_cases _ -> invalid_arg "Multiple calls to Jsont.Object.case_mem"
11241124+ | _ ->
11251125+ match dec_absent with
11261126+ | None -> ()
11271127+ | Some tag ->
11281128+ (* Check that we have a case definition for it *)
11291129+ let equal_t (Case case) = tag_compare case.tag tag = 0 in
11301130+ if not (List.exists equal_t cases) then
11311131+ let tag = match tag_to_string with
11321132+ | None -> "" | Some tag_to_string -> " " ^ tag_to_string tag
11331133+ in
11341134+ invalid_arg ("No case for dec_absent case member value" ^ tag)
11351135+11361136+ let case_tag_mem ?(doc = "") name type' ~dec_absent ~enc_omit =
11371137+ let id = Type.Id.make () in
11381138+ let enc t = t (* N.B. this fact may be used by encoders. *) in
11391139+ let enc_omit = match enc_omit with
11401140+ | None -> Fun.const false | Some omit -> omit
11411141+ in
11421142+ { name; doc; type'; id; dec_absent; enc; enc_omit }
11431143+11441144+ let case_mem
11451145+ ?doc ?(tag_compare = Stdlib.compare) ?tag_to_string ?dec_absent
11461146+ ?enc ?enc_omit ?enc_case name type' cases map
11471147+ =
11481148+ let () = check_case_mem map cases ~dec_absent ~tag_compare ~tag_to_string in
11491149+ let tag = case_tag_mem ?doc name type' ~dec_absent ~enc_omit in
11501150+ let enc = match enc with None -> Mem.no_enc name | Some e -> e in
11511151+ let enc_case = match enc_case with
11521152+ | Some enc_case -> enc_case
11531153+ | None ->
11541154+ fun _case ->
11551155+ Error.msgf Meta.none "No case encoder for member %a" pp_code name
11561156+ in
11571157+ let id = Type.Id.make () in
11581158+ let cases = {tag; tag_compare; tag_to_string; id; cases; enc; enc_case} in
11591159+ let dec = Dec_app (map.dec, id) in
11601160+ { map with dec; shape = Object_cases (None, cases) }
11611161+11621162+ (* Unknown members *)
11631163+11641164+ module Mems = struct
11651165+ type ('mems, 'a) enc =
11661166+ { enc :
11671167+ 'acc. (Meta.t -> string -> 'a -> 'acc -> 'acc) -> 'mems -> 'acc ->
11681168+ 'acc }
11691169+11701170+ type ('mems, 'a, 'builder) map = ('mems, 'a, 'builder) mems_map
11711171+11721172+ let mems_kind kind = Sort.kinded' ~kind "members map"
11731173+ let map
11741174+ ?(kind = "") ?(doc = "") ?dec_empty ?dec_add ?dec_finish
11751175+ ?enc mems_type
11761176+ =
11771177+ let dec_empty = match dec_empty with
11781178+ | Some dec_empty -> dec_empty
11791179+ | None -> fun () -> Error.no_decoder Meta.none ~kind:(mems_kind kind)
11801180+ in
11811181+ let dec_add = match dec_add with
11821182+ | Some dec_add -> dec_add
11831183+ | None -> fun _ _ _ _ -> Error.no_decoder Meta.none ~kind:(mems_kind kind)
11841184+ in
11851185+ let dec_finish = match dec_finish with
11861186+ | Some dec_finish -> dec_finish
11871187+ | None -> fun _ _ -> Error.no_decoder Meta.none ~kind:(mems_kind kind)
11881188+ in
11891189+ let enc = match enc with
11901190+ | Some { enc } -> enc
11911191+ | None -> fun _ _ _ -> Error.no_encoder Meta.none ~kind:(mems_kind kind)
11921192+ in
11931193+ let id = Type.Id.make () in
11941194+ { kind; doc; mems_type; id; dec_empty; dec_add; dec_finish; enc }
11951195+11961196+ let string_map ?kind ?doc type' =
11971197+ let dec_empty () = String_map.empty in
11981198+ let dec_add _meta n v mems = String_map.add n v mems in
11991199+ let dec_finish _meta mems = mems in
12001200+ let enc f mems acc =
12011201+ String_map.fold (fun n v acc -> f Meta.none n v acc) mems acc
12021202+ in
12031203+ map ?kind ?doc type' ~dec_empty ~dec_add ~dec_finish ~enc:{enc}
12041204+ end
12051205+12061206+ let set_shape_unknown_mems shape u = match shape with
12071207+ | Object_basic (Unknown_keep _) | Object_cases (Some (Unknown_keep _), _) ->
12081208+ invalid_arg "Jsont.Object.keep_unknown already called on object"
12091209+ | Object_basic _ -> Object_basic u
12101210+ | Object_cases (_, cases) -> Object_cases (Some u, cases)
12111211+12121212+ let skip_unknown map =
12131213+ { map with shape = set_shape_unknown_mems map.shape Unknown_skip }
12141214+12151215+ let error_unknown map =
12161216+ { map with shape = set_shape_unknown_mems map.shape Unknown_error }
12171217+12181218+ let mems_noenc (mems : (_, _, _) mems_map) _o =
12191219+ let kind = Sort.kinded' ~kind:mems.kind "members" in
12201220+ Error.no_encoder Meta.none ~kind
12211221+12221222+ let keep_unknown ?enc mems (map : ('o, 'dec) object_map) =
12231223+ let enc = match enc with None -> mems_noenc mems | Some enc -> enc in
12241224+ let dec = Dec_app (map.dec, mems.id) in
12251225+ let unknown = Unknown_keep (mems, enc) in
12261226+ { map with dec; shape = set_shape_unknown_mems map.shape unknown }
12271227+12281228+ let zero = finish (map ~kind:"zero" ())
12291229+12301230+ let as_string_map ?kind ?doc t =
12311231+ map ?kind ?doc Fun.id
12321232+ |> keep_unknown (Mems.string_map t) ~enc:Fun.id
12331233+ |> finish
12341234+end
12351235+12361236+(* Ignoring *)
12371237+12381238+let ignore =
12391239+ let kind = "ignore" in
12401240+ let dec_null = Repr.Null Base.ignore and dec_bool = Repr.Bool Base.ignore in
12411241+ let dec_number = Repr.Number Base.ignore in
12421242+ let dec_string = Repr.String Base.ignore in
12431243+ let dec_array = Array.ignore and dec_object = Object.zero in
12441244+ let enc _v = Error.no_encoder Meta.none ~kind in
12451245+ any ~kind ~dec_null ~dec_bool ~dec_number ~dec_string ~dec_array ~dec_object
12461246+ ~enc ()
12471247+12481248+let zero =
12491249+ let kind = "zero" in
12501250+ let null = null () and dec_bool = Repr.Bool Base.ignore in
12511251+ let dec_number = Repr.Number Base.ignore in
12521252+ let dec_string = Repr.String Base.ignore in
12531253+ let dec_array = Array.ignore and dec_object = Object.zero in
12541254+ let enc () = null in
12551255+ any ~kind ~dec_null:null ~dec_bool ~dec_number ~dec_string ~dec_array
12561256+ ~dec_object ~enc ()
12571257+12581258+let todo ?(kind = "") ?doc ?dec_stub () =
12591259+ let dec = match dec_stub with
12601260+ | Some v -> Fun.const v
12611261+ | None -> fun _v -> Error.decode_todo Meta.none ~kind_opt:kind
12621262+ in
12631263+ let enc _v = Error.encode_todo Meta.none ~kind_opt:kind in
12641264+ map ~kind ?doc ~dec ~enc ignore
12651265+12661266+(* Generic JSON *)
12671267+12681268+type name = string node
12691269+type mem = name * json
12701270+and object' = mem list
12711271+and json =
12721272+| Null of unit node
12731273+| Bool of bool node
12741274+| Number of float node
12751275+| String of string node
12761276+| Array of json list node
12771277+| Object of object' node
12781278+12791279+let pp_null = Fmt.json_null
12801280+let pp_bool = Fmt.json_bool
12811281+let pp_string = Fmt.json_string
12821282+let pp_number = Fmt.json_number
12831283+let pp_number' = Fmt.json_number'
12841284+let pp_json' ?(number_format = Fmt.json_default_number_format) () ppf j =
12851285+ let pp_indent = 2 in
12861286+ let pp_sep ppf () =
12871287+ Format.pp_print_char ppf ',';
12881288+ Format.pp_print_break ppf 1 pp_indent
12891289+ in
12901290+ let rec pp_array ppf a =
12911291+ Format.pp_open_hovbox ppf 0;
12921292+ Format.pp_print_char ppf '[';
12931293+ Format.pp_print_break ppf 0 pp_indent;
12941294+ (Format.pp_print_list ~pp_sep pp_value) ppf a;
12951295+ Format.pp_print_break ppf 0 0;
12961296+ Format.pp_print_char ppf ']';
12971297+ Format.pp_close_box ppf ()
12981298+ and pp_mem ppf ((m, _), v) =
12991299+ Format.pp_open_hvbox ppf 0;
13001300+ pp_string ppf m; Format.pp_print_string ppf ": "; pp_value ppf v;
13011301+ Format.pp_close_box ppf ();
13021302+ and pp_obj ppf o =
13031303+ Format.pp_open_hvbox ppf 0;
13041304+ Format.pp_print_char ppf '{';
13051305+ Format.pp_print_break ppf 0 pp_indent;
13061306+ (Format.pp_print_list ~pp_sep pp_mem) ppf o;
13071307+ Format.pp_print_break ppf 0 0;
13081308+ Format.pp_print_char ppf '}';
13091309+ Format.pp_close_box ppf ();
13101310+ and pp_value ppf = function
13111311+ | Null _ -> pp_null ppf ()
13121312+ | Bool (b,_ ) -> pp_bool ppf b
13131313+ | Number (f, _) -> pp_number' number_format ppf f
13141314+ | String (s, _) -> pp_string ppf s
13151315+ | Array (a, _) -> pp_array ppf a
13161316+ | Object (o, _) -> pp_obj ppf o
13171317+ in
13181318+ pp_value ppf j
13191319+13201320+let pp_json ppf j = pp_json' () ppf j
13211321+13221322+(* Generic JSON *)
13231323+13241324+module Json = struct
13251325+ type 'a cons = ?meta:Meta.t -> 'a -> json
13261326+ type t = json
13271327+13281328+ let meta = function
13291329+ | Null (_, m) -> m | Bool (_, m) -> m | Number (_, m) -> m
13301330+ | String (_, m) -> m | Array (_, m) -> m | Object (_, m) -> m
13311331+13321332+ let set_meta m = function
13331333+ | Null (v, _) -> Null (v, m) | Bool (v, _) -> Bool (v, m)
13341334+ | Number (v, _) -> Number (v, m) | String (v, _) -> String (v, m)
13351335+ | Array (v, _) -> Array (v, m) | Object (v, _) -> Object (v, m)
13361336+13371337+ let get_meta = meta
13381338+ let copy_layout v ~dst =
13391339+ set_meta (Meta.copy_ws (meta v) ~dst:(meta dst)) dst
13401340+13411341+ let sort = function
13421342+ | Null _ -> Sort.Null | Bool _ -> Sort.Bool | Number _ -> Sort.Number
13431343+ | String _ -> Sort.String | Array _ -> Sort.Array | Object _ -> Sort.Object
13441344+13451345+ let rec compare (j0 : json) (j1 : json) = match j0, j1 with
13461346+ | Null ((), _), Null ((), _) -> 0
13471347+ | Null _, _ -> -1 | _, Null _ -> 1
13481348+ | Bool (b0, _), Bool (b1, _) -> Bool.compare b0 b1
13491349+ | Bool _, _ -> -1 | _, Bool _ -> 1
13501350+ | Number (f0, _), Number (f1, _) -> Float.compare f0 f1
13511351+ | Number _, _ -> -1 | _, Number _ -> 1
13521352+ | String (s0, _), String (s1, _) -> String.compare s0 s1
13531353+ | String _, _ -> -1 | _, String _ -> 1
13541354+ | Array (a0, _), (Array (a1, _)) -> List.compare compare a0 a1
13551355+ | Array _, _ -> -1 | _, Array _ -> 1
13561356+ | Object (o0, _), Object (o1, _) ->
13571357+ let order_mem ((n0, _), _) ((n1, _), _) = String.compare n0 n1 in
13581358+ let compare_mem ((n0, _), j0) ((n1, _), j1) =
13591359+ let c = String.compare n0 n1 in
13601360+ if c = 0 then compare j0 j1 else c
13611361+ in
13621362+ List.compare compare_mem (List.sort order_mem o0) (List.sort order_mem o1)
13631363+13641364+ let equal j0 j1 = compare j0 j1 = 0
13651365+ let pp = pp_json
13661366+13671367+ (* Nulls and options *)
13681368+13691369+ let null' = Null ((), Meta.none)
13701370+ let null ?(meta = Meta.none) () = Null ((), meta)
13711371+ let option c ?meta = function None -> null ?meta () | Some v -> c ?meta v
13721372+13731373+ (* Booleans *)
13741374+13751375+ let bool ?(meta = Meta.none) b = Bool (b, meta)
13761376+13771377+ (* Numbers *)
13781378+13791379+ let number ?(meta = Meta.none) n = Number (n, meta)
13801380+ let any_float ?(meta = Meta.none) v =
13811381+ if Float.is_finite v
13821382+ then Number (v, meta)
13831383+ else String (Float.to_string v, meta)
13841384+13851385+ let int32 ?(meta = Meta.none) v = Number (Int32.to_float v, meta)
13861386+ let int64_as_string ?(meta = Meta.none) v = String (Int64.to_string v, meta)
13871387+ let int64 ?(meta = Meta.none) v =
13881388+ if Jsont_base.Number.can_store_exact_int64 v
13891389+ then Number (Int64.to_float v, meta)
13901390+ else String (Int64.to_string v, meta)
13911391+13921392+ let int_as_string ?(meta = Meta.none) i = String (Int.to_string i, meta)
13931393+ let int ?(meta = Meta.none) v =
13941394+ if Jsont_base.Number.can_store_exact_int v
13951395+ then Number (Int.to_float v, meta)
13961396+ else String (Int.to_string v, meta)
13971397+13981398+ (* Strings *)
13991399+14001400+ let string ?(meta = Meta.none) s = String (s, meta)
14011401+14021402+ (* Arrays *)
14031403+14041404+ let list ?(meta = Meta.none) l = Array (l, meta)
14051405+ let array ?(meta = Meta.none) a = Array (Stdlib.Array.to_list a, meta)
14061406+ let empty_array = list []
14071407+14081408+ (* Objects *)
14091409+14101410+ let name ?(meta = Meta.none) n = n, meta
14111411+ let mem n v = n, v
14121412+ let object' ?(meta = Meta.none) mems = Object (mems, meta)
14131413+ let empty_object = object' []
14141414+14151415+ let rec find_mem n = function
14161416+ | [] -> None
14171417+ | ((n', _), _ as m) :: ms ->
14181418+ if String.equal n n' then Some m else find_mem n ms
14191419+14201420+ let find_mem' (n, _) ms = find_mem n ms
14211421+ let object_names mems = List.map (fun ((n, _), _) -> n) mems
14221422+ let object_names' mems = List.map fst mems
14231423+14241424+ (* Zero *)
14251425+14261426+ let zero ?meta j = match sort j with
14271427+ | Null -> null ?meta () | Bool -> bool ?meta false
14281428+ | Number -> number ?meta 0. | String -> string ?meta ""
14291429+ | Array -> list ?meta [] | Object -> object' ?meta []
14301430+14311431+ (* Converting *)
14321432+14331433+ open Repr
14341434+14351435+ let error_sort ~exp j = Error.sort (meta j) ~exp ~fnd:(sort j)
14361436+ let error_type t fnd =
14371437+ Error.kinded_sort (meta fnd) ~exp:(kinded_sort t) ~fnd:(sort fnd)
14381438+14391439+ let find_all_unexpected ~mem_decs mems =
14401440+ let unexpected ((n, _ as nm), _v) =
14411441+ match Repr.String_map.find_opt n mem_decs with
14421442+ | None -> Some nm | Some _ -> None
14431443+ in
14441444+ List.filter_map unexpected mems
14451445+14461446+ (* Decoding *)
14471447+14481448+ let rec decode : type a. a Repr.t -> json -> a =
14491449+ fun t j -> match t with
14501450+ | Null map ->
14511451+ (match j with Null (n, meta) -> map.dec meta n | j -> error_type t j)
14521452+ | Bool map ->
14531453+ (match j with Bool (b, meta) -> map.dec meta b | j -> error_type t j)
14541454+ | Number map ->
14551455+ (match j with
14561456+ | Number (n, meta) -> map.dec meta n
14571457+ | Null (_, meta) -> map.dec meta Float.nan
14581458+ | j -> error_type t j)
14591459+ | String map ->
14601460+ (match j with String (s, meta) -> map.dec meta s | j -> error_type t j)
14611461+ | Array map ->
14621462+ (match j with
14631463+ | Array (vs, meta) -> decode_array map meta vs
14641464+ | j -> error_type t j)
14651465+ | Object map ->
14661466+ (match j with
14671467+ | Object (mems, meta) -> decode_object map meta mems
14681468+ | j -> error_type t j)
14691469+ | Map map -> map.dec (decode map.dom j)
14701470+ | Any map -> decode_any t map j
14711471+ | Rec t -> decode (Lazy.force t) j
14721472+14731473+ and decode_array :
14741474+ type a elt b. (a, elt, b) array_map -> Meta.t -> json list -> a
14751475+ =
14761476+ fun map meta vs ->
14771477+ let rec next (map : (a, elt, b) array_map) meta b i = function
14781478+ | [] -> map.dec_finish meta i b
14791479+ | v :: vs ->
14801480+ let b =
14811481+ try
14821482+ if map.dec_skip i b then b else
14831483+ map.dec_add i (decode map.elt v) b
14841484+ with Error e -> Repr.error_push_array meta map (i, get_meta v) e
14851485+ in
14861486+ next map meta b (i + 1) vs
14871487+ in
14881488+ next map meta (map.dec_empty ()) 0 vs
14891489+14901490+ and decode_object : type o. (o, o) Object.map -> Meta.t -> object' -> o =
14911491+ fun map meta mems ->
14921492+ let dict = Dict.empty in
14931493+ let umems = Unknown_mems None in
14941494+ apply_dict map.dec @@
14951495+ decode_object_map map meta umems String_map.empty String_map.empty dict mems
14961496+14971497+ and decode_object_map : type o.
14981498+ (o, o) Object.map -> Meta.t -> unknown_mems_option ->
14991499+ mem_dec String_map.t -> mem_dec String_map.t -> Dict.t -> object' -> Dict.t
15001500+ =
15011501+ fun map meta umems mem_miss mem_decs dict mems ->
15021502+ let u _ _ _ = assert false in
15031503+ let mem_miss = String_map.union u mem_miss map.mem_decs in
15041504+ let mem_decs = String_map.union u mem_decs map.mem_decs in
15051505+ match map.shape with
15061506+ | Object_cases (umems', cases) ->
15071507+ let umems' = Unknown_mems umems' in
15081508+ let umems, dict = Repr.override_unknown_mems ~by:umems umems' dict in
15091509+ decode_object_cases map meta umems cases mem_miss mem_decs dict [] mems
15101510+ | Object_basic umems' ->
15111511+ let umems' = Unknown_mems (Some umems') in
15121512+ let umems, dict = Repr.override_unknown_mems ~by:umems umems' dict in
15131513+ match umems with
15141514+ | Unknown_mems (Some Unknown_skip | None) ->
15151515+ let umems = Unknown_skip in
15161516+ decode_object_basic map meta umems () mem_miss mem_decs dict mems
15171517+ | Unknown_mems (Some (Unknown_error as umems)) ->
15181518+ decode_object_basic map meta umems () mem_miss mem_decs dict mems
15191519+ | Unknown_mems (Some (Unknown_keep (umap, _) as umems)) ->
15201520+ let umap = umap.dec_empty () in
15211521+ decode_object_basic map meta umems umap mem_miss mem_decs dict mems
15221522+15231523+ and decode_object_basic : type o p m b.
15241524+ (o, o) object_map -> Meta.t -> (p, m, b) unknown_mems -> b ->
15251525+ mem_dec String_map.t -> mem_dec String_map.t -> Dict.t -> object' -> Dict.t
15261526+ =
15271527+ fun map meta umems umap mem_miss mem_decs dict -> function
15281528+ | [] -> Repr.finish_object_decode map meta umems umap mem_miss dict
15291529+ | ((n, nmeta as nm), v) :: mems ->
15301530+ match String_map.find_opt n mem_decs with
15311531+ | Some (Mem_dec m) ->
15321532+ let dict = try Dict.add m.id (decode m.type' v) dict with
15331533+ | Error e -> Repr.error_push_object meta map nm e
15341534+ in
15351535+ let mem_miss = String_map.remove n mem_miss in
15361536+ decode_object_basic map meta umems umap mem_miss mem_decs dict mems
15371537+ | None ->
15381538+ match umems with
15391539+ | Unknown_skip ->
15401540+ decode_object_basic
15411541+ map meta umems umap mem_miss mem_decs dict mems
15421542+ | Unknown_error ->
15431543+ let fnd = nm :: find_all_unexpected ~mem_decs mems in
15441544+ Repr.unexpected_mems_error meta map ~fnd
15451545+ | Unknown_keep (umap', _) ->
15461546+ let umap =
15471547+ try umap'.dec_add nmeta n (decode umap'.mems_type v) umap with
15481548+ | Error e -> Repr.error_push_object meta map nm e
15491549+ in
15501550+ decode_object_basic
15511551+ map meta umems umap mem_miss mem_decs dict mems
15521552+15531553+ and decode_object_cases : type o cs t.
15541554+ (o, o) object_map -> Meta.t -> unknown_mems_option ->
15551555+ (o, cs, t) object_cases -> mem_dec String_map.t -> mem_dec String_map.t ->
15561556+ Dict.t -> object' -> object' -> Dict.t
15571557+ =
15581558+ fun map meta umems cases mem_miss mem_decs dict delay mems ->
15591559+ let decode_case_tag map meta tag delay mems =
15601560+ let eq_tag (Case c) = cases.tag_compare c.tag tag = 0 in
15611561+ match List.find_opt eq_tag cases.cases with
15621562+ | None -> Repr.unexpected_case_tag_error meta map cases tag
15631563+ | Some (Case case) ->
15641564+ let mems = List.rev_append delay mems in
15651565+ let dict =
15661566+ decode_object_map
15671567+ case.object_map meta umems mem_miss mem_decs dict mems
15681568+ in
15691569+ Dict.add
15701570+ cases.id (case.dec (apply_dict case.object_map.dec dict)) dict
15711571+ in
15721572+ match mems with
15731573+ | [] ->
15741574+ (match cases.tag.dec_absent with
15751575+ | Some tag -> decode_case_tag map meta tag delay []
15761576+ | None ->
15771577+ let kinded_sort = Repr.object_map_kinded_sort map in
15781578+ Error.missing_mems meta ~kinded_sort
15791579+ ~exp:[cases.tag.name]
15801580+ ~fnd:(List.map (fun ((n, _), _) -> n) delay))
15811581+ | ((n, meta as nm), v as mem) :: mems ->
15821582+ if n = cases.tag.name then
15831583+ let tag = try decode cases.tag.type' v with
15841584+ | Error e -> Repr.error_push_object meta map nm e
15851585+ in
15861586+ decode_case_tag map meta tag delay mems
15871587+ else
15881588+ match String_map.find_opt n mem_decs with
15891589+ | None ->
15901590+ let delay = mem :: delay in
15911591+ decode_object_cases
15921592+ map meta umems cases mem_miss mem_decs dict delay mems
15931593+ | Some (Mem_dec m) ->
15941594+ let dict = try Dict.add m.id (decode m.type' v) dict with
15951595+ | Error e -> Repr.error_push_object meta map nm e
15961596+ in
15971597+ let mem_miss = String_map.remove n mem_miss in
15981598+ decode_object_cases
15991599+ map meta umems cases mem_miss mem_decs dict delay mems
16001600+16011601+ and decode_any : type a. a Repr.t -> a any_map -> json -> a =
16021602+ fun t map j ->
16031603+ let dec t map j = match map with
16041604+ | Some t -> decode t j | None -> error_type t j
16051605+ in
16061606+ match j with
16071607+ | Null _ -> dec t map.dec_null j
16081608+ | Bool _ -> dec t map.dec_bool j
16091609+ | Number _ -> dec t map.dec_number j
16101610+ | String _ -> dec t map.dec_string j
16111611+ | Array _ -> dec t map.dec_array j
16121612+ | Object _ -> dec t map.dec_object j
16131613+16141614+ let dec = decode
16151615+ let decode' t j = try Ok (decode t j) with Error e -> Result.Error e
16161616+ let decode t j = Result.map_error Error.to_string (decode' t j)
16171617+16181618+ (* Encode *)
16191619+16201620+ let rec encode : type a. a Repr.t -> a -> json =
16211621+ fun t v -> match t with
16221622+ | Null map -> null ~meta:(map.enc_meta v) (map.enc v)
16231623+ | Bool map -> bool ~meta:(map.enc_meta v) (map.enc v)
16241624+ | Number map -> number ~meta:(map.enc_meta v) (map.enc v)
16251625+ | String map -> string ~meta:(map.enc_meta v) (map.enc v)
16261626+ | Array map ->
16271627+ let enc map acc i elt =
16281628+ try encode map.elt elt :: acc with
16291629+ | Error e -> Repr.error_push_array Meta.none map (i, Meta.none) e
16301630+ in
16311631+ list ~meta:(map.enc_meta v) (List.rev (map.enc (enc map) [] v))
16321632+ | Object map ->
16331633+ let mems = encode_object map ~do_unknown:true v [] in
16341634+ Object (List.rev mems, map.enc_meta v)
16351635+ | Any map -> encode (map.enc v) v
16361636+ | Map map -> encode map.dom (map.enc v)
16371637+ | Rec t -> encode (Lazy.force t) v
16381638+16391639+ and encode_object : type o dec.
16401640+ (o, o) object_map -> do_unknown:bool -> o -> object' -> object'
16411641+ =
16421642+ fun map ~do_unknown o obj ->
16431643+ let encode_mem map obj (Mem_enc mmap) =
16441644+ try
16451645+ let v = mmap.enc o in
16461646+ if mmap.enc_omit v then obj else
16471647+ ((mmap.name, Meta.none), encode mmap.type' v) :: obj
16481648+ with
16491649+ | Error e -> Repr.error_push_object Meta.none map (mmap.name, Meta.none) e
16501650+ in
16511651+ let obj = List.fold_left (encode_mem map) obj map.mem_encs in
16521652+ match map.shape with
16531653+ | Object_basic (Unknown_keep (umap, enc)) when do_unknown ->
16541654+ encode_unknown_mems map umap (enc o) obj
16551655+ | Object_basic _ -> obj
16561656+ | Object_cases (u, cases) ->
16571657+ let Case_value (case, c) = cases.enc_case (cases.enc o) in
16581658+ let obj =
16591659+ let n = cases.tag.name, Meta.none in
16601660+ try
16611661+ if cases.tag.enc_omit case.tag then obj else
16621662+ (n, encode cases.tag.type' case.tag) :: obj
16631663+ with
16641664+ | Error e -> Repr.error_push_object Meta.none map n e
16651665+ in
16661666+ match u with
16671667+ | Some (Unknown_keep (umap, enc)) ->
16681668+ (* Less T.R. but feels nicer to encode unknowns at the end *)
16691669+ let obj = encode_object case.object_map ~do_unknown:false c obj in
16701670+ encode_unknown_mems map umap (enc o) obj
16711671+ | _ -> encode_object case.object_map ~do_unknown c obj
16721672+16731673+ and encode_unknown_mems : type o dec mems a builder.
16741674+ (o, o) object_map -> (mems, a, builder) mems_map -> mems -> object' ->
16751675+ object'
16761676+ =
16771677+ fun map umap mems obj ->
16781678+ let encode_mem map meta name v obj =
16791679+ let n = (name, meta) in
16801680+ let v = try encode umap.mems_type v with
16811681+ | Error e -> Repr.error_push_object Meta.none map n e
16821682+ in
16831683+ (n, v) :: obj
16841684+ in
16851685+ (umap.enc (encode_mem map) mems obj)
16861686+16871687+ let enc = encode
16881688+ let encode' t v = try Ok (encode t v) with Error e -> Result.Error e
16891689+ let encode t v = Result.map_error Error.to_string (encode' t v)
16901690+16911691+ (* Recode *)
16921692+16931693+ let update t v = enc t (dec t v)
16941694+ let recode' t v = try Ok (update t v) with Error e -> Result.Error e
16951695+ let recode t v = Result.map_error Error.to_string (recode' t v)
16961696+end
16971697+16981698+let json_null =
16991699+ let dec meta () = Json.null ~meta () in
17001700+ let enc = function
17011701+ | Null ((), _) -> () | j -> Json.error_sort ~exp:Sort.Null j
17021702+ in
17031703+ Repr.Null (Base.map ~dec ~enc ~enc_meta:Json.meta ())
17041704+17051705+let json_bool =
17061706+ let dec meta b = Json.bool ~meta b in
17071707+ let enc = function
17081708+ | Bool (b, _) -> b | j -> Json.error_sort ~exp:Sort.Bool j
17091709+ in
17101710+ Repr.Bool (Base.map ~dec ~enc ~enc_meta:Json.meta ())
17111711+17121712+let json_number =
17131713+ let dec meta n = Json.number ~meta n in
17141714+ let enc = function
17151715+ | Number (n, _) -> n | j -> Json.error_sort ~exp:Sort.Number j
17161716+ in
17171717+ Repr.Number (Base.map ~dec ~enc ~enc_meta:Json.meta ())
17181718+17191719+let json_string =
17201720+ let dec meta s = Json.string ~meta s in
17211721+ let enc = function
17221722+ | String (s, _) -> s | j -> Json.error_sort ~exp:Sort.String j
17231723+ in
17241724+ Repr.String (Base.map ~dec ~enc ~enc_meta:Json.meta ())
17251725+17261726+let json, json_array, mem_list, json_object =
17271727+ let rec elt = Repr.Rec any
17281728+ and array_map = lazy begin
17291729+ let dec_empty () = [] in
17301730+ let dec_add _i v a = v :: a in
17311731+ let dec_finish meta _len a = Json.list ~meta (List.rev a) in
17321732+ let enc f acc = function
17331733+ | Array (a, _) -> Array.list_enc f acc a
17341734+ | j -> Json.error_sort ~exp:Sort.Array j
17351735+ in
17361736+ let enc = { Array.enc = enc } in
17371737+ Array.map ~dec_empty ~dec_add ~dec_finish ~enc ~enc_meta:Json.meta elt
17381738+ end
17391739+17401740+ and array = lazy (Array.array (Lazy.force array_map))
17411741+ and mems = lazy begin
17421742+ let dec_empty () = [] in
17431743+ let dec_add meta n v mems = ((n, meta), v) :: mems in
17441744+ let dec_finish _meta mems = List.rev mems in
17451745+ let enc f l a = List.fold_left (fun a ((n, m), v) -> f m n v a) a l in
17461746+ let enc = { Object.Mems.enc = enc } in
17471747+ Object.Mems.map ~dec_empty ~dec_add ~dec_finish ~enc elt
17481748+ end
17491749+17501750+ and object' = lazy begin
17511751+ let enc_meta = function
17521752+ | Object (_, meta) -> meta | j -> Json.error_sort ~exp:Sort.Object j
17531753+ in
17541754+ let enc = function
17551755+ | Object (mems, _) -> mems | j -> Json.error_sort ~exp:Sort.Object j
17561756+ in
17571757+ let dec meta mems = Object (mems, meta) in
17581758+ Object.map' dec ~enc_meta
17591759+ |> Object.keep_unknown (Lazy.force mems) ~enc
17601760+ |> Object.finish
17611761+ end
17621762+17631763+ and any = lazy begin
17641764+ let json_array = Lazy.force array in
17651765+ let json_object = Lazy.force object' in
17661766+ let enc = function
17671767+ | Null _ -> json_null | Bool _ -> json_bool
17681768+ | Number _ -> json_number | String _ -> json_string
17691769+ | Array _ -> json_array | Object _ -> json_object
17701770+ in
17711771+ Repr.Any { kind = "json"; doc = "";
17721772+ dec_null = Some json_null; dec_bool = Some json_bool;
17731773+ dec_number = Some json_number; dec_string = Some json_string;
17741774+ dec_array = Some json_array;
17751775+ dec_object = Some json_object; enc }
17761776+ end
17771777+ in
17781778+ Lazy.force any, Lazy.force array, Lazy.force mems, Lazy.force object'
17791779+17801780+let json_mems =
17811781+ let dec_empty () = [] in
17821782+ let dec_add meta name v mems = ((name, meta), v) :: mems in
17831783+ let dec_finish meta mems = Object (List.rev mems, meta) in
17841784+ let enc f j acc = match j with
17851785+ | Object (ms, _) -> List.fold_left (fun acc ((n, m), v) -> f m n v acc) acc ms
17861786+ | j -> Json.error_sort ~exp:Sort.Object j
17871787+ in
17881788+ let enc = { Object.Mems.enc = enc } in
17891789+ Object.Mems.map ~dec_empty ~dec_add ~dec_finish ~enc json
17901790+17911791+(* Queries and updates *)
17921792+17931793+(* val app : ('a -> 'b) t -> 'a t -> 'b t
17941794+ val product : 'a t -> 'b t -> ('a * 'b) t
17951795+ val bind : 'a t -> ('a -> 'b t) -> 'b t
17961796+ val map : ('a -> 'b) -> 'a t -> 'b t *)
17971797+17981798+let const t v =
17991799+ let const _ = v in
18001800+ let dec = map ~dec:const ignore in
18011801+ let enc = map ~enc:const t in
18021802+ let enc _v = enc in
18031803+ any ~dec_null:dec ~dec_bool:dec ~dec_number:dec ~dec_string:dec ~dec_array:dec
18041804+ ~dec_object:dec ~enc ()
18051805+18061806+let recode ~dec:dom f ~enc =
18071807+ let m = map ~dec:f dom in
18081808+ let enc _v = enc in
18091809+ any ~dec_null:m ~dec_bool:m ~dec_number:m ~dec_string:m ~dec_array:m
18101810+ ~dec_object:m ~enc ()
18111811+18121812+let update t =
18131813+ let dec v = Json.update t v in
18141814+ Repr.Map { kind = ""; doc = ""; dom = json; dec; enc = Fun.id }
18151815+18161816+(* Array queries *)
18171817+18181818+let rec list_repeat n v l = if n <= 0 then l else list_repeat (n - 1) v (v :: l)
18191819+18201820+let nth ?absent n t =
18211821+ let dec_empty () = None in
18221822+ let dec_skip i _v = i <> n in
18231823+ let dec_add _i v _acc = Some v in
18241824+ let dec_finish meta len v = match v with
18251825+ | Some v -> v
18261826+ | None ->
18271827+ match absent with
18281828+ | Some v -> v
18291829+ | None -> Error.index_out_of_range meta ~n ~len
18301830+ in
18311831+ let enc f acc v = f acc 0 v in
18321832+ let enc = { Array.enc } in
18331833+ Array.array (Array.map ~dec_empty ~dec_skip ~dec_add ~dec_finish ~enc t)
18341834+18351835+let update_nth ?stub ?absent n t =
18361836+ let update_elt n t v = Json.copy_layout v ~dst:(Json.update t v) in
18371837+ let rec update_array ~seen n t i acc = function
18381838+ | v :: vs when i = n ->
18391839+ let elt = update_elt (i, Json.meta v) t v in
18401840+ update_array ~seen:true n t (i + 1) (elt :: acc) vs
18411841+ | v :: vs -> update_array ~seen n t (i + 1) (v :: acc) vs
18421842+ | [] when seen -> Either.Right (List.rev acc)
18431843+ | [] -> Either.Left (acc, i)
18441844+ in
18451845+ let update ?stub ?absent n t j = match j with
18461846+ | Array (vs, meta) ->
18471847+ begin match update_array ~seen:false n t 0 [] vs with
18481848+ | Either.Right elts -> Array (elts, meta)
18491849+ | Either.Left (acc, len) ->
18501850+ match absent with
18511851+ | None -> Error.index_out_of_range meta ~n ~len
18521852+ | Some absent ->
18531853+ let elt = Json.enc t absent in
18541854+ let stub = match stub with
18551855+ | None -> Json.zero elt | Some j -> j
18561856+ in
18571857+ Array (List.rev (elt :: list_repeat (n - len) stub acc), meta)
18581858+ end
18591859+ | j -> Json.error_sort ~exp:Sort.Array j
18601860+ in
18611861+ let dec = update ?stub ?absent n t in
18621862+ let enc j = j in
18631863+ map ~dec ~enc json
18641864+18651865+let set_nth ?stub ?(allow_absent = false) t n v =
18661866+ let absent = if allow_absent then Some v else None in
18671867+ update_nth ?stub ?absent n (const t v)
18681868+18691869+let delete_nth ?(allow_absent = false) n =
18701870+ let dec_empty () = [] in
18711871+ let dec_add i v a = if i = n then a else (v :: a) in
18721872+ let dec_finish meta len a =
18731873+ if n < len || allow_absent then Json.list ~meta (List.rev a) else
18741874+ Error.index_out_of_range meta ~n ~len
18751875+ in
18761876+ let enc f acc = function
18771877+ | Array (a, _) -> Array.list_enc f acc a
18781878+ | j -> Json.error_sort ~exp:Sort.Array j
18791879+ in
18801880+ let enc_meta j = Json.meta j in
18811881+ let enc = { Array.enc = enc } in
18821882+ Array.array (Array.map ~dec_empty ~dec_add ~dec_finish ~enc ~enc_meta json)
18831883+18841884+let filter_map_array a b f =
18851885+ let dec_empty () = [] in
18861886+ let dec_add i v acc = match f i (Json.dec a v) with
18871887+ | None -> acc | Some v' -> (Json.enc b v') :: acc
18881888+ in
18891889+ let dec_finish meta _len acc = Json.list ~meta (List.rev acc) in
18901890+ let enc f acc = function
18911891+ | Array (a, _) -> Array.list_enc f acc a
18921892+ | j -> Json.error_sort ~exp:Sort.Array j
18931893+ in
18941894+ let enc = { Array.enc = enc } in
18951895+ let enc_meta j = Json.meta j in
18961896+ Array.array (Array.map ~dec_empty ~dec_add ~dec_finish ~enc ~enc_meta json)
18971897+18981898+let fold_array t f acc =
18991899+ let dec_empty () = acc in
19001900+ let dec_add = f in
19011901+ let dec_finish _meta _len acc = acc in
19021902+ let enc _f acc _a = acc in
19031903+ let enc = { Array.enc = enc } in
19041904+ Array.array (Array.map ~dec_empty ~dec_add ~dec_finish ~enc t)
19051905+19061906+(* Object queries *)
19071907+19081908+let mem ?absent name t =
19091909+ Object.map Fun.id
19101910+ |> Object.mem name t ~enc:Fun.id ?dec_absent:absent
19111911+ |> Object.finish
19121912+19131913+let update_mem ?absent name t =
19141914+ let update_mem n t v = n, Json.copy_layout v ~dst:(Json.update t v) in
19151915+ let rec update_object ~seen name t acc = function
19161916+ | ((name', _ as n), v) :: mems when String.equal name name' ->
19171917+ update_object ~seen:true name t (update_mem n t v :: acc) mems
19181918+ | mem :: mems -> update_object ~seen name t (mem :: acc) mems
19191919+ | [] when seen -> Either.Right (List.rev acc)
19201920+ | [] -> Either.Left acc
19211921+ in
19221922+ let update ?absent name t = function
19231923+ | Object (mems, meta) ->
19241924+ let mems = match update_object ~seen:false name t [] mems with
19251925+ | Either.Right mems -> mems
19261926+ | Either.Left acc ->
19271927+ match absent with
19281928+ | None ->
19291929+ let fnd = Json.object_names mems in
19301930+ Error.missing_mems meta ~kinded_sort:"" ~exp:[name] ~fnd
19311931+ | Some absent ->
19321932+ let m = (name, Meta.none), Json.enc t absent in
19331933+ List.rev (m :: acc)
19341934+ in
19351935+ Object (mems, meta)
19361936+ | j -> Json.error_sort ~exp:Sort.Object j
19371937+ in
19381938+ let update = update ?absent name t in
19391939+ let enc j = j in
19401940+ map ~dec:update ~enc json
19411941+19421942+let set_mem ?(allow_absent = false) t name v =
19431943+ let absent = if allow_absent then Some v else None in
19441944+ update_mem ?absent name (const t v)
19451945+19461946+let update_json_object ~name ~dec_add ~dec_finish =
19471947+ let mems =
19481948+ let dec_empty () = false, [] in
19491949+ let enc f (_, l) a = List.fold_left (fun a ((n, m), v) -> f m n v a) a l in
19501950+ let enc = { Object.Mems.enc = enc } in
19511951+ Object.Mems.map ~dec_empty ~dec_add ~dec_finish ~enc json
19521952+ in
19531953+ let enc_meta = function
19541954+ | Object (_, meta) -> meta | j -> Json.error_sort ~exp:Sort.Object j
19551955+ in
19561956+ let enc = function
19571957+ | Object (mems, _) -> false, mems | j -> Json.error_sort ~exp:Sort.Object j
19581958+ in
19591959+ let dec meta (ok, mems) =
19601960+ let fnd = Json.object_names mems in
19611961+ if not ok
19621962+ then Error.missing_mems meta ~kinded_sort:"" ~exp:[name] ~fnd else
19631963+ Object (List.rev mems, meta)
19641964+ in
19651965+ Object.map' dec ~enc_meta
19661966+ |> Object.keep_unknown mems ~enc
19671967+ |> Object.finish
19681968+19691969+let delete_mem ?(allow_absent = false) name =
19701970+ let dec_add meta n v (ok, mems) =
19711971+ if n = name then true, mems else ok, ((n, meta), v) :: mems
19721972+ in
19731973+ let dec_finish _meta (ok, ms as a) = if allow_absent then (true, ms) else a in
19741974+ update_json_object ~name ~dec_add ~dec_finish
19751975+19761976+let fold_object t f acc =
19771977+ let mems =
19781978+ let dec_empty () = acc and dec_add = f and dec_finish _meta acc = acc in
19791979+ let enc f _ acc = acc in
19801980+ Object.Mems.map t ~dec_empty ~dec_add ~dec_finish ~enc:{ Object.Mems.enc }
19811981+ in
19821982+ Object.map Fun.id
19831983+ |> Object.keep_unknown mems ~enc:Fun.id
19841984+ |> Object.finish
19851985+19861986+let filter_map_object a b f =
19871987+ let dec_add meta n v (_, mems) =
19881988+ match f meta n (Json.dec a v) with
19891989+ | None -> (true, mems)
19901990+ | Some (n', v') -> (true, (n', (Json.enc b v')) :: mems)
19911991+ in
19921992+ let dec_finish _meta acc = acc in
19931993+ update_json_object ~name:"" (* irrelevant *) ~dec_add ~dec_finish
19941994+19951995+(* Index queries *)
19961996+19971997+let index ?absent i t = match i with
19981998+| Path.Nth (n, _) -> nth ?absent n t
19991999+| Path.Mem (n, _) -> mem ?absent n t
20002000+20012001+let set_index ?allow_absent t i v = match i with
20022002+| Path.Nth (n, _) -> set_nth ?allow_absent t n v
20032003+| Path.Mem (n, _) -> set_mem ?allow_absent t n v
20042004+20052005+let update_index ?stub ?absent i t = match i with
20062006+| Path.Nth (n, _) -> update_nth ?stub ?absent n t
20072007+| Path.Mem (n, _) -> update_mem ?absent n t
20082008+20092009+let delete_index ?allow_absent = function
20102010+| Path.Nth (n, _) -> delete_nth ?allow_absent n
20112011+| Path.Mem (n, _) -> delete_mem ?allow_absent n
20122012+20132013+(* Path queries *)
20142014+20152015+let path ?absent p q =
20162016+ List.fold_left (fun q i -> index ?absent i q) q (Path.rev_indices p)
20172017+20182018+let update_path ?stub ?absent p t = match Path.rev_indices p with
20192019+| [] -> update t
20202020+| i :: is ->
20212021+ match absent with
20222022+ | None ->
20232023+ let update t i = update_index i t in
20242024+ List.fold_left update (update_index i t) is
20252025+ | Some absent ->
20262026+ let rec loop absent t = function
20272027+ | Path.Nth (n, _) :: is ->
20282028+ loop Json.empty_array (update_nth ~absent n t) is
20292029+ | Path.Mem (n, _) :: is ->
20302030+ loop Json.empty_object (update_mem ~absent n t) is
20312031+ | [] -> t
20322032+ in
20332033+ match i with
20342034+ | Path.Nth (n, _) ->
20352035+ loop Json.empty_array (update_nth ?stub ~absent n t) is
20362036+ | Path.Mem (n, _) ->
20372037+ loop Json.empty_object (update_mem ~absent n t) is
20382038+20392039+let delete_path ?allow_absent p = match Path.rev_indices p with
20402040+| [] -> recode ~dec:ignore (fun () -> Json.null') ~enc:json
20412041+| i :: is ->
20422042+ let upd del i = update_index i del in
20432043+ List.fold_left upd (delete_index ?allow_absent i) is
20442044+20452045+let set_path ?stub ?(allow_absent = false) t p v = match Path.rev_indices p with
20462046+| [] -> recode ~dec:ignore (fun () -> Json.enc t v) ~enc:json
20472047+| i :: is ->
20482048+ let absent = if allow_absent then Some v else None in
20492049+ update_path ?stub ?absent p (const t v)
20502050+20512051+(* Formatting *)
20522052+20532053+type format = Minify | Indent | Layout
20542054+type number_format = Fmt.json_number_format
20552055+let default_number_format = Fmt.json_default_number_format
20562056+let pp_value ?number_format t () = fun ppf v -> match Json.encode t v with
20572057+| Ok j -> pp_json' ?number_format () ppf j
20582058+| Error e -> pp_string ppf e
+2056
vendor/opam/jsont/src/jsont.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Types for JSON values.
77+88+ This module provides a type for describing subsets of JSON values as
99+ bidirectional maps with arbitrary OCaml values. We call these
1010+ values {e JSON types}.
1111+1212+ In these maps the {e decoding} direction maps from JSON values to
1313+ OCaml values and the {e encoding} direction maps from OCaml values
1414+ to JSON values. Depending on your needs, one direction or the
1515+ other can be left unspecified. Some of the decoding maps may be
1616+ lossy or creative which leads to JSON queries and transforms.
1717+1818+ Read the {{!page-index.quick_start}quick start} and the
1919+ {{!page-cookbook}cookbook}. *)
2020+2121+(** {1:preliminaries Preliminaries} *)
2222+2323+type 'a fmt = Format.formatter -> 'a -> unit
2424+(** The type for formatters of values of type ['a]. *)
2525+2626+(** Text locations.
2727+2828+ A text location identifies a text span in a given UTF-8 encoded file
2929+ by an inclusive range of absolute {{!Textloc.type-byte_pos}byte} positions
3030+ and the {{!Textloc.type-line_pos}line positions} on which those occur. *)
3131+module Textloc : sig
3232+3333+ (** {1:fpath File paths} *)
3434+3535+ type fpath = string
3636+ (** The type for file paths. *)
3737+3838+ val file_none : fpath
3939+ (** [file_none] is ["-"]. A file path to use when there is none. *)
4040+4141+ (** {1:pos Positions} *)
4242+4343+ (** {2:byte_pos Byte positions} *)
4444+4545+ type byte_pos = int
4646+ (** The type for zero-based, absolute, byte positions in text. If
4747+ the text has [n] bytes, [0] is the first position and [n-1] is
4848+ the last position. *)
4949+5050+ val byte_pos_none : byte_pos
5151+ (** [byte_pos_none] is [-1]. A position to use when there is none. *)
5252+5353+ (** {2:lines Lines} *)
5454+5555+ type line_num = int
5656+ (** The type for one-based, line numbers in the text. Lines
5757+ increment after a {e newline} which is either a line feed ['\n']
5858+ (U+000A), a carriage return ['\r'] (U+000D) or a carriage return
5959+ and a line feed ["\r\n"] (<U+000D,U+000A>). *)
6060+6161+ val line_num_none : line_num
6262+ (** [line_num_none] is [-1]. A line number to use when there is none. *)
6363+6464+ (** {2:line_pos Line positions} *)
6565+6666+ type line_pos = line_num * byte_pos
6767+ (** The type for line positions. This identifies a line by its line
6868+ number and the absolute byte position following its newline
6969+ (or the start of text for the first line). That byte position:
7070+ {ul
7171+ {- Indexes the first byte of text of the line if the line is non-empty.}
7272+ {- Indexes the first byte of the next {e newline} sequence if the line
7373+ is empty.}
7474+ {- Is out of bounds and equal to the text's length for a last empty
7575+ line. This is also the case on empty text.}} *)
7676+7777+ val line_pos_first : line_pos
7878+ (** [line_pos_first] is [1, 0]. Note that this is the only line position
7979+ of the empty text. *)
8080+8181+ val line_pos_none : line_pos
8282+ (** [line_pos_none] is [(line_pos_none, pos_pos_none)]. *)
8383+8484+ (** {1:tloc Text locations} *)
8585+8686+ type t
8787+ (** The type for text locations. A text location identifies a text
8888+ span in an UTF-8 encoded file by an inclusive range of absolute
8989+ {{!type-byte_pos}byte positions} and the {{!type-line_pos}line
9090+ positions} on which they occur.
9191+9292+ If the first byte equals the last byte the range contains
9393+ exactly that byte. If the first byte is greater than the last
9494+ byte this represents an insertion point before the first
9595+ byte. In this case information about the last position should
9696+ be ignored: it can contain anything. *)
9797+9898+ val none : t
9999+ (** [none] is a position to use when there is none. *)
100100+101101+ val make :
102102+ file:fpath -> first_byte:byte_pos -> last_byte:byte_pos ->
103103+ first_line:line_pos -> last_line:line_pos -> t
104104+ (** [v ~file ~first_byte ~last_byte ~first_line ~last_line] is a text
105105+ location with the given arguments, see corresponding accessors for
106106+ the semantics. If you don't have a file use {!file_none}. *)
107107+108108+ val file : t -> fpath
109109+ (** [file l] is [l]'s file. *)
110110+111111+ val set_file : t -> fpath -> t
112112+ (** [set_file l file] is [l] with {!file} set to [file]. *)
113113+114114+ val first_byte : t -> byte_pos
115115+ (** [first_byte l] is [l]'s first byte. Irrelevant if {!is_none} is
116116+ [true]. *)
117117+118118+ val last_byte : t -> byte_pos
119119+ (** [last_byte l] is [l]'s last byte. Irrelevant if {!is_none} or
120120+ {!is_empty} is [true]. *)
121121+122122+ val first_line : t -> line_pos
123123+ (** [first_line l] is the line position on which [first_byte l] lies.
124124+ Irrelevant if {!is_none} is [true].*)
125125+126126+ val last_line : t -> line_pos
127127+ (** [last_line l] is the line position on which [last_byte l] lies.
128128+ Irrelevant if {!is_none} or {!is_empty} is [true].*)
129129+130130+ (** {2:preds Predicates and comparisons} *)
131131+132132+ val is_none : t -> bool
133133+ (** [is_none t] is [true] iff [first_byte < 0]. *)
134134+135135+ val is_empty : t -> bool
136136+ (** [is_empty t] is [true] iff [first_byte t > last_byte t]. *)
137137+138138+ val equal : t -> t -> bool
139139+ (** [equal t0 t1] is [true] iff [t0] and [t1] are equal. This checks
140140+ that {!file}, {!first_byte} and {!last_byte} are equal. Line information
141141+ is ignored. *)
142142+143143+ val compare : t -> t -> int
144144+ (** [compare t0 t1] orders [t0] and [t1]. The order is compatible
145145+ with {!equal}. Comparison starts with {!file}, follows with
146146+ {!first_byte} and ends, if needed, with {!last_byte}. Line
147147+ information is ignored. *)
148148+149149+ (** {2:shrink_and_stretch Shrink and stretch} *)
150150+151151+ val set_first : t -> first_byte:byte_pos -> first_line:line_pos -> t
152152+ (** [set_first l ~first_byte ~first_line] sets the the first position of
153153+ [l] to given values. *)
154154+155155+ val set_last : t -> last_byte:byte_pos -> last_line:line_pos -> t
156156+ (** [set_last l ~last_byte ~last_line] sets the last position of [l]
157157+ to given values. *)
158158+159159+ val to_first : t -> t
160160+ (** [to_first l] has both first and last positions set to [l]'s first
161161+ position. The range spans {!first_byte}. See also {!before}. *)
162162+163163+ val to_last : t -> t
164164+ (** [to_last l] has both first and last positions set to [l]'s last
165165+ position. The range spans {!last_byte}. See also {!after}. *)
166166+167167+ val before : t -> t
168168+ (** [before t] is the {{!is_empty}empty} text location starting at
169169+ {!first_byte}. *)
170170+171171+ val after : t -> t
172172+ (** [after t] is the empty {{!is_empty}empty} location starting at
173173+ [last_byte t + 1]; note that at the end of input this may be an
174174+ invalid byte {e index}. The {!first_line} and {!last_line} of the
175175+ result is [last_line t]. *)
176176+177177+ val span : t -> t -> t
178178+ (** [span l0 l1] is the span from the smallest byte position of [l0] and
179179+ [l1] to the largest byte position of [l0] and [l1]. The file path is
180180+ taken from the greatest byte position. *)
181181+182182+ val reloc : first:t -> last:t -> t
183183+ (** [reloc ~first ~last] uses the first position of [first], the
184184+ last position of [last] and the file of [last]. *)
185185+186186+ (** {2:fmt Formatting} *)
187187+188188+ val pp_ocaml : Format.formatter -> t -> unit
189189+ (** [pp_ocaml] formats text locations like the OCaml compiler. *)
190190+191191+ val pp_gnu : Format.formatter -> t -> unit
192192+ (** [pp_gnu] formats text locations according to the
193193+ {{:https://www.gnu.org/prep/standards/standards.html#Errors}GNU
194194+ convention}. *)
195195+196196+ val pp : Format.formatter -> t -> unit
197197+ (** [pp] is {!pp_ocaml}. *)
198198+199199+ val pp_dump : Format.formatter -> t -> unit
200200+ (** [pp_dump] formats raw data for debugging. *)
201201+end
202202+203203+(** Abstract syntax tree node metadata.
204204+205205+ This type keeps information about source text locations
206206+ and whitespace. *)
207207+module Meta : sig
208208+ type t
209209+ (** The type for node metadata. *)
210210+211211+ val make : ?ws_before:string -> ?ws_after:string -> Textloc.t -> t
212212+ (** [make textloc ~ws_before ~ws_after] is metadata with text location
213213+ [textloc] whitespace [ws_before] before the node and [ws_after] after
214214+ the node. Both default to [""]. *)
215215+216216+ val none : t
217217+ (** [none] is metadata for when there is none. Its {!textloc}
218218+ is {!Textloc.none} and its whitespace is empty. *)
219219+220220+ val is_none : t -> bool
221221+ (** [is_none m] is [true] iff [m] is {!none}. *)
222222+223223+ val textloc : t -> Textloc.t
224224+ (** [textloc m] is the text location of [m]. *)
225225+226226+ val ws_before : t -> string
227227+ (** [ws_before m] is source whitespace before the node. *)
228228+229229+ val ws_after : t -> string
230230+ (** [ws_after m] is source whitespace after the node. *)
231231+232232+ val with_textloc : t -> Textloc.t -> t
233233+ (** [with_textloc m l] is [m] with text location [l] *)
234234+235235+ val clear_ws : t -> t
236236+ (** [clear_ws m] is [m] with {!ws_before} and {!ws_after} set to [""]. *)
237237+238238+ val clear_textloc : t -> t
239239+ (** [clear_textloc m] is [m] with {!textloc} set to {!Textloc.none}. *)
240240+241241+ val copy_ws : t -> dst:t -> t
242242+ (** [copy_ws src ~dst] copies {!ws_before} and {!ws_after} of [src]
243243+ to [dst]. *)
244244+end
245245+246246+type 'a node = 'a * Meta.t
247247+(** The type for abstract syntax tree nodes.
248248+ The node data of type ['a] and its metadata. *)
249249+250250+(** JSON paths.
251251+252252+ Paths are used for keeping track of erroring
253253+ {{!Error.Context.t}contexts} and for specifying {{!Jsont.queries}
254254+ query and update}
255255+ locations. *)
256256+module Path : sig
257257+258258+ (** {1:indices Indices} *)
259259+260260+ type index =
261261+ | Mem of string node (** Indexes the value of the member [n] of an object. *)
262262+ | Nth of int node (** Indexes the value of the [n]th element of an array. *)
263263+ (** The type for indexing operations on JSON values. *)
264264+265265+ val pp_index : index fmt
266266+ (** [pp_index] formats indexes. *)
267267+268268+ val pp_index_trace : index fmt
269269+ (** [pp_index] formats indexes and their location. *)
270270+271271+ (** {1:path Paths} *)
272272+273273+ type t
274274+ (** The type for paths, a sequence of indexing operations. *)
275275+276276+ val root : t
277277+ (** [root] is the root path. *)
278278+279279+ val is_root : t -> bool
280280+ (** [is_root p] is [true] iff [p] is the root path. *)
281281+282282+ val nth : ?meta:Meta.t -> int -> t -> t
283283+ (** [nth n p] indexes the array indexed by [p] at index [n]. *)
284284+285285+ val mem : ?meta:Meta.t -> string -> t -> t
286286+ (** [mem n p] indexes the object indexed by [p] at member [n]. *)
287287+288288+ val rev_indices : t -> index list
289289+ (** [rev_indices p] are the indices of [p] in reverse order, the last
290290+ indexing operation appears first. *)
291291+292292+ val of_string : string -> (t, string) result
293293+ (** [of_string s] parses a path according to the
294294+ {{!Path.path_syntax}path syntax}. *)
295295+296296+ val pp : t fmt
297297+ (** [pp] formats paths. *)
298298+299299+ val pp_trace : t fmt
300300+ (** [pp_trace] formats paths as a stack trace, if not empty. *)
301301+302302+ (** {1:path_syntax Path syntax}
303303+304304+ Path provide a way for end users to address JSON and edit locations.
305305+306306+ A {e path} is a sequence of member and list indexing
307307+ operations. Applying the path to a JSON value leads to either a
308308+ JSON value, or nothing if one of the indices does not exist, or
309309+ an error if ones tries to index a non-indexable value.
310310+311311+ Here are a few examples of paths.
312312+313313+ {@json[
314314+ {
315315+ "ocaml": {
316316+ "libs": ["jsont", "brr", "cmdliner"]
317317+ }
318318+ }
319319+ ]}
320320+321321+ {@shell[
322322+ ocaml.libs # value of member "libs" of member "ocaml"
323323+ ocaml.libs.[0] # first element of member "libs" of member "ocaml"
324324+ ]}
325325+326326+ More formally a {e path} is a [.] seperated list of indices. An
327327+ {e index} is written [[i]]. [i] can a zero-based list index. Or
328328+ [i] can be an object member name [n]. If there is no ambiguity,
329329+ the surrounding brackets can be dropped.
330330+331331+ {b Notes.}
332332+ {ul
333333+ {- The syntax has no form of quoting at the moment this
334334+ means key names can't contain, [\[], [\]], or start with a number.}
335335+ {- It would be nice to be able to drop the dots in order
336336+ to be compatible with {{:https://www.rfc-editor.org/rfc/rfc9535}
337337+ JSONPath} syntax.}
338338+ {- Reintroduce and implement negative indices (they are parsed).}} *)
339339+end
340340+341341+(** Sorts of JSON values. *)
342342+module Sort : sig
343343+ type t =
344344+ | Null (** Nulls *)
345345+ | Bool (** Booleans *)
346346+ | Number (** Numbers *)
347347+ | String (** Strings *)
348348+ | Array (** Arrays *)
349349+ | Object (** Objects *)
350350+ (** The type for sorts of JSON values. *)
351351+352352+ val to_string : t -> string
353353+ (** [to_string sort] is a string for sort [sort]. *)
354354+355355+ val pp : Format.formatter -> t -> unit
356356+ (** [pp] formats sorts. *)
357357+358358+ (** {1:kinds Kinds}
359359+360360+ For formatting error messages. *)
361361+362362+ val or_kind : kind:string -> t -> string
363363+ (** [or_kind ~kind sort] is [to_string sort] if [kind] is [""] and
364364+ [kind] otherwise. *)
365365+366366+ val kinded : kind:string -> t -> string
367367+ (** [kinded ~kind sort] is [to_string sort] if [kind] is [""]
368368+ and [String.concat " " [kind; to_string sort]] otherwise. *)
369369+370370+ val kinded' : kind:string -> string -> string
371371+ (** [kinded' ~kind sort] is [sort] if [kind] is [""]
372372+ and [String.concat " " [kind; sort]] otherwise. *)
373373+end
374374+375375+(** Encoding, decoding and query errors. *)
376376+module Error : sig
377377+378378+ (** {1:kinds Kinds of errors} *)
379379+380380+ type kind
381381+ (** The type for kind of errors. *)
382382+383383+ val kind_to_string : kind -> string
384384+ (** [kind_to_string kind] is [kind] as a string. *)
385385+386386+ (** {1:errors Errors} *)
387387+388388+ (** JSON error contexts. *)
389389+ module Context : sig
390390+391391+ type index = string node * Path.index
392392+ (** The type for context indices. The {{!Jsont.kinded_sort}kinded sort} of
393393+ an array or object and its index. *)
394394+395395+ type t = index list
396396+ (** The type for erroring contexts. The first element indexes the
397397+ root JSON value. *)
398398+399399+ val empty : t
400400+ (** [empty] is the empty context. *)
401401+402402+ val is_empty : t -> bool
403403+ (** [is_empty ctx] is [true] iff [ctx] is {!empty}. *)
404404+405405+ val push_array : string node -> int node -> t -> t
406406+ (** [push_array kinded_sort n ctx] wraps [ctx] as the [n]th element of an
407407+ array of {{!Jsont.kinded_sort}kinded sort} [kinded_sort]. *)
408408+409409+ val push_object : string node -> string node -> t -> t
410410+ (** [push_object kinded_sort n ctx] wraps [ctx] as the member named [n] of
411411+ an object of {{!Jsont.kinded_sort}kinded sort} [kinded_sort]. *)
412412+ end
413413+414414+ type t = Context.t * Meta.t * kind
415415+ (** The type for errors. The context, the error localisation and the
416416+ kind of error. *)
417417+418418+ val raise : Context.t -> Meta.t -> kind -> 'a
419419+ (** [raise ctx meta k] raises an error with given paramters. *)
420420+421421+ val make_msg : Context.t -> Meta.t -> string -> t
422422+ (** [make_msg ctx meta msg] is an error with message [msg] for meta [meta]
423423+ in context [ctx]. *)
424424+425425+ val msg : Meta.t -> string -> 'a
426426+ (** [msg meta msg] raises an error with message [msg] for meta
427427+ [meta] in an empty context. *)
428428+429429+ val msgf : Meta.t -> ('a, Stdlib.Format.formatter, unit, 'b) format4 -> 'a
430430+ (** [msgf meta fmt …] is like {!val-msg} but formats an error message. *)
431431+432432+ val expected : Meta.t -> string -> fnd:string -> 'a
433433+ (** [expected meta fmt exp ~fnd] is
434434+ [msgf "Expected %s but found %s" exp fnd]. *)
435435+436436+ val push_array : string node -> int node -> t -> 'a
437437+ (** [push_array kinded_sort n e] contextualises [e] as an error in the
438438+ [n]th element of an array of {{!Jsont.kinded_sort}kinded sort}
439439+ [kinded_sort]. *)
440440+441441+ val push_object : string node -> string node -> t -> 'a
442442+ (** [push_object kinded_sort n e] contextualises [e] as an error in
443443+ the member [n] of an object of {{!Jsont.kinded_sort}kinded sort}
444444+ [kinded_sort]. *)
445445+446446+ val adjust_context :
447447+ first_byte:Textloc.byte_pos -> first_line:Textloc.line_pos -> t -> 'a
448448+ (** [adjust_context ~first_byte ~first_line] adjusts the error's
449449+ context's meta to encompass the given positions. *)
450450+451451+ (** {1:fmt Formatting} *)
452452+453453+ val to_string : t -> string
454454+ (** [error_to_string e] formats [e] using {!val-pp} to a string. *)
455455+456456+ val pp : t fmt
457457+ (** [pp_error] formats errors. *)
458458+459459+ val puterr : unit fmt
460460+ (** [puterr] formats [Error:] in red. *)
461461+462462+ (**/**)
463463+ val disable_ansi_styler : unit -> unit
464464+ (**/**)
465465+end
466466+467467+exception Error of Error.t
468468+(** The exception raised on map errors. In general codec and query
469469+ functions turn that for you into a {!result} value. *)
470470+471471+(** {1:types Types} *)
472472+473473+type 'a t
474474+(** The type for JSON types.
475475+476476+ A value of this type represents a subset of JSON values mapped to
477477+ a subset of values of type ['a] and vice versa. *)
478478+479479+val kinded_sort : 'a t -> string
480480+(** [kinded_sort t] is a human readable string describing the JSON
481481+ values typed by [t]. This combines the kind of the map with the
482482+ {{!Sort}sort}(s) of JSON value mapped by [t]. For example if [t]
483483+ is an object map and the kind specified for the
484484+ {{!Object.val-map}map} is ["T"] then this is ["T object"], if the
485485+ kind is empty this is simply ["object"]. See also
486486+ {!Sort.kinded}. *)
487487+488488+val kind : 'a t -> string
489489+(** [kind t] is the [kind] of the underlying map. If the kind is an
490490+ empty string this falls back to mention the {{!Sort}sort}. For
491491+ example if [t] is an object map and the kind specified for the
492492+ {{!Object.val-map}map} is ["T"] then this is ["T"], if the kind is
493493+ empty then this is ["object"]. See also {!Sort.or_kind}. *)
494494+495495+val doc : 'a t -> string
496496+(** [doc t] is a documentation string for the JSON values typed by [t]. *)
497497+498498+val with_doc : ?kind:string -> ?doc:string -> 'a t -> 'a t
499499+(** [with_doc ?kind ?doc t] is [t] with its {!doc} or {!kind}
500500+ updated to the corresponding values if specified. *)
501501+502502+(** {1:base Base types}
503503+504504+ Read the {{!page-cookbook.base_types}cookbook} on base types. *)
505505+506506+(** Mapping JSON base types. *)
507507+module Base : sig
508508+509509+ (** {1:maps Maps} *)
510510+511511+ type ('a, 'b) map
512512+ (** The type for mapping JSON values of type ['a] to values of type ['b]. *)
513513+514514+ val map :
515515+ ?kind:string -> ?doc:string -> ?dec:(Meta.t -> 'a -> 'b) ->
516516+ ?enc:('b -> 'a) -> ?enc_meta:('b -> Meta.t) ->
517517+ unit -> ('a, 'b) map
518518+ (** [map ~kind ~doc ~dec ~enc ~enc_meta ()] maps JSON base types
519519+ represented by value of type ['a] to values of type ['b] with:
520520+ {ul
521521+ {- [kind] names the entities represented by the map and [doc]
522522+ documents them. Both default to [""].}
523523+ {- [dec] is used to decode values of type ['a] to values of
524524+ type ['b]. Can be omitted if the map is only used for
525525+ encoding, the default unconditionally errors.}
526526+ {- [enc] is used to encode values of type ['b] to values of
527527+ type ['a]. Can be omitted if the map is only used for
528528+ decoding, the default unconditionally errors.}
529529+ {- [enc_meta] is used to recover JSON metadata (source text
530530+ layout information) from a value to encode. The default
531531+ unconditionnaly returns {!Jsont.Meta.none}.}}
532532+533533+ {{!decenc}These functions} can be used to quickly devise
534534+ [dec] and [enc] functions from standard OCaml conversion
535535+ interfaces. *)
536536+537537+ val id : ('a, 'a) map
538538+ (** [id] is the identity map. *)
539539+540540+ val ignore : ('a, unit) map
541541+ (** [ignore] is the ignoring map. It ignores decodes and errors on
542542+ encodes. *)
543543+544544+ (** {2:types JSON types} *)
545545+546546+ val null : (unit, 'a) map -> 'a t
547547+ (** [null map] maps with [map] JSON nulls represented by [()] to
548548+ values of type ['a]. See also {!Jsont.null}. *)
549549+550550+ val bool : (bool, 'a) map -> 'a t
551551+ (** [bool map] maps with [map] JSON booleans represented by [bool]
552552+ values to values of type ['a]. See also {!Jsont.bool}. *)
553553+554554+ val number : (float, 'a) map -> 'a t
555555+ (** [number map] maps with [map] JSON nulls or numbers represented by
556556+ [float] values to values of type ['a]. The [float]
557557+ representation decodes JSON nulls to {!Float.nan} and lossily
558558+ encodes any {{!Float.is_finite}non-finite} to JSON null
559559+ ({{!page-cookbook.non_finite_numbers}explanation}). See also
560560+ {!Jsont.number}. *)
561561+562562+ val string : (string, 'a) map -> 'a t
563563+ (** [string map] maps with [map] {e unescaped} JSON strings represented
564564+ by UTF-8 encoded [string] values to values of type ['a]. See
565565+ also {!Jsont.string}. *)
566566+567567+ (** {1:decenc Decoding and encoding functions}
568568+569569+ These function create suitable [dec] and [enc] functions
570570+ to give to {!val-map} from standard OCaml conversion interfaces.
571571+ See also {!Jsont.of_of_string}. *)
572572+573573+ val dec : ('a -> 'b) -> (Meta.t -> 'a -> 'b)
574574+ (** [dec f] is a decoding function from [f]. This assumes [f] never fails. *)
575575+576576+ val dec_result :
577577+ ?kind:string -> ('a -> ('b, string) result) -> (Meta.t -> 'a -> 'b)
578578+ (** [dec f] is a decoding function from [f]. [Error _] values are given to
579579+ {!Error.msg}, prefixed by [kind:] (if specified). *)
580580+581581+ val dec_failure : ?kind:string -> ('a -> 'b) -> (Meta.t -> 'a -> 'b)
582582+ (** [dec f] is a decoding function from [f]. [Failure _] exceptions
583583+ are catched and given to {!Error.msg}, prefixed by [kind:] (if
584584+ specified). *)
585585+586586+ val enc : ('b -> 'a) -> ('b -> 'a)
587587+ (** [enc f] is an encoding function from [f]. This assumes [f] never fails. *)
588588+589589+ val enc_result : ?kind:string -> ('b -> ('a, string) result) -> ('b -> 'a)
590590+ (** [enc_result f] is an encoding function from [f]. [Error _] values are
591591+ given to {!Error.msg}, prefixed by [kind:] (if specified). *)
592592+593593+ val enc_failure : ?kind:string -> ('b -> 'a) -> ('b -> 'a)
594594+ (** [enc_failure f] is an encoding function from [f]. [Failure _]
595595+ exceptions are catched and given to {!Error.msg}, prefixed by [kind:]
596596+ (if specified). *)
597597+end
598598+599599+(** {2:option Nulls and options}
600600+601601+ Read the {{!page-cookbook.dealing_with_null}cookbook} on [null]s. *)
602602+603603+val null : ?kind:string -> ?doc:string -> 'a -> 'a t
604604+(** [null v] maps JSON nulls to [v]. On encodes any value of type ['a]
605605+ is encoded by null. [doc] and [kind] are given to the underlying
606606+ {!Base.type-map}. See also {!Base.null}. *)
607607+608608+val none : 'a option t
609609+(** [none] maps JSON nulls to [None]. *)
610610+611611+val some : 'a t -> 'a option t
612612+(** [some t] maps JSON like [t] does but wraps results in [Some].
613613+ Encoding fails if the value is [None]. *)
614614+615615+val option : ?kind:string -> ?doc:string -> 'a t -> 'a option t
616616+(** [option t] maps JSON nulls to [None] and other values by [t].
617617+ [doc] and [kind] are given to the underlying {!val-any} map. *)
618618+619619+(** {2:booleans Booleans} *)
620620+621621+val bool : bool t
622622+(** [bool] maps JSON booleans to [bool] values. See also {!Base.bool}. *)
623623+624624+(** {2:numbers Numbers}
625625+626626+ Read the {{!page-cookbook.dealing_with_numbers}cookbook} on JSON
627627+ numbers and their many pitfalls. *)
628628+629629+val number : float t
630630+(** [number] maps JSON nulls or numbers to [float] values. On decodes
631631+ JSON null is mapped to {!Float.nan}. On encodes any
632632+ {{!Float.is_finite}non-finite} float is lossily mapped to JSON
633633+ null ({{!page-cookbook.non_finite_numbers}explanation}). See also
634634+ {!Base.number}, {!any_float} and the integer combinators below. *)
635635+636636+val any_float : float t
637637+(** [any_float] is a lossless representation for IEEE 754 doubles. It
638638+ maps {{!Float.is_finite}non-finite} floats by the JSON strings
639639+ defined by {!Float.to_string}. This contrasts with {!val-number}
640640+ which maps them to JSON null values
641641+ ({{!page-cookbook.non_finite_numbers}explanation}). Note that on
642642+ decodes this still maps JSON nulls to {!Float.nan} and any
643643+ successful string decode of {!Float.of_string_opt} (so numbers can
644644+ also be written as strings). See also {!val-number}.
645645+646646+ {b Warning.} [any_float] should only be used between parties that
647647+ have agreed on such an encoding. To maximize interoperability you
648648+ should use the lossy {!val-number} map. *)
649649+650650+val float_as_hex_string : float t
651651+(** [float_as_hex_string] maps JSON strings made of IEEE 754 doubles in hex
652652+ notation to float values. On encodes strings this uses the ["%h"]
653653+ format string. On decodes it accepts anything sucessfully decoded
654654+ by {!Float.of_string_opt}. *)
655655+656656+val uint8 : int t
657657+(** [uint8] maps JSON numbers to unsigned 8-bit integers. JSON numbers
658658+ are sucessfully decoded if after truncation they can be represented
659659+ on the \[0;255\] range. Encoding errors if the integer is out of
660660+ range.*)
661661+662662+val uint16 : int t
663663+(** [uint16] maps JSON numbers to unsigned 16-bit integers. JSON numbers
664664+ are sucessfully decoded if after truncation they can be represented
665665+ on the \[0;65535\] range. Encoding errors if the integer is out of
666666+ range.*)
667667+668668+val int8 : int t
669669+(** [int8] maps JSON numbers to 8-bit integers. JSON numbers
670670+ are sucessfully decoded if after truncation they can be represented
671671+ on the \[-128;127\] range. Encoding errors if the integer is out of
672672+ range.*)
673673+674674+val int16 : int t
675675+(** [int16] maps JSON numbers to 16-bit integers. JSON numbers
676676+ are sucessfully decoded if after truncation they can be represented
677677+ on the \[-32768;32767\] range. Encoding errors if the integer is out
678678+ of range. *)
679679+680680+val int32 : int32 t
681681+(** [int32] maps JSON numbers to 32-bit integers. JSON numbers
682682+ are sucessfully decoded if after truncation they can be represented
683683+ on the [int32] range, otherwise the decoder errors. *)
684684+685685+val int64 : int64 t
686686+(** [int] maps truncated JSON numbers or JSON strings to 64-bit
687687+ integers.
688688+ {ul
689689+ {- JSON numbers are sucessfully decoded if after truncation they can
690690+ be represented on the [int64] range, otherwise the decoder
691691+ errors. [int64] values are encoded as JSON numbers if the
692692+ integer is in the \[-2{^53};2{^53}\] range.}
693693+ {- JSON strings are decoded using {!int_of_string_opt}, this
694694+ allows binary, octal, decimal and hex syntaxes and errors on
695695+ overflow and syntax errors. [int] values are encoded as JSON
696696+ strings with {!Int.to_string} when the integer is outside the
697697+ \[-2{^53};2{^53}\] range}} *)
698698+699699+val int64_as_string : int64 t
700700+(** [int64_as_string] maps JSON strings to 64-bit integers. On decodes
701701+ this uses {!Int64.of_string_opt} which allows binary, octal,
702702+ decimal and hex syntaxes and errors on overflow and syntax
703703+ errors. On encodes uses {!Int64.to_string}. *)
704704+705705+val int : int t
706706+(** [int] maps truncated JSON numbers or JSON strings to [int] values.
707707+ {ul
708708+ {- JSON numbers are sucessfully decoded if after truncation they can
709709+ be represented on the [int] range, otherwise the decoder
710710+ errors. [int] values are encoded as JSON numbers if the
711711+ integer is in the \[-2{^53};2{^53}\] range.}
712712+ {- JSON strings are decoded using {!int_of_string_opt}, this
713713+ allows binary, octal, decimal and hex syntaxes and errors on
714714+ overflow and syntax errors. [int] values are encoded as JSON
715715+ strings with {!Int.to_string} when the integer is outside the
716716+ \[-2{^53};2{^53}\] range}}
717717+718718+ {b Warning.} The behaviour of this function is platform
719719+ dependent, it depends on the value of {!Sys.int_size}. *)
720720+721721+val int_as_string : int t
722722+(** [int_as_string] maps JSON strings to [int] values. On
723723+ decodes this uses {!int_of_string_opt} which allows binary,
724724+ octal, decimal and hex syntaxes and errors on overflow and
725725+ syntax errors. On encodes uses {!Int.to_string}.
726726+727727+ {b Warning.} The behaviour of this function is platform
728728+ dependent, it depends on the value of {!Sys.int_size}. *)
729729+730730+(** {2:enums Strings and enums}
731731+732732+ Read the {{!page-cookbook.transform_strings}cookbook} on
733733+ transforming strings. *)
734734+735735+val string : string t
736736+(** [string] maps unescaped JSON strings to UTF-8 encoded [string]
737737+ values. See also {!Base.string}.
738738+739739+ {b Warning.} Encoders assume OCaml [string]s have been checked for
740740+ UTF-8 validity. *)
741741+742742+val of_of_string : ?kind:string -> ?doc:string ->
743743+ ?enc:('a -> string) -> (string -> ('a, string) result) -> 'a t
744744+(** [of_of_string of_string] maps JSON string with a
745745+ {{!Base.type-map}base map} using [of_string] for decoding and [enc] for
746746+ encoding. See the {{!page-cookbook.transform_strings}cookbook}. *)
747747+748748+val enum :
749749+ ?cmp:('a -> 'a -> int) -> ?kind:string -> ?doc:string ->
750750+ (string * 'a) list -> 'a t
751751+(** [enum assoc] maps JSON strings member of the [assoc] list to the
752752+ corresponding OCaml value and vice versa in log(n).
753753+ [cmp] is used to compare the OCaml values, it defaults to {!Stdlib.compare}.
754754+ Decoding and encoding errors on strings or values not part of
755755+ [assoc] *)
756756+757757+val binary_string : string t
758758+(** [binary_string] maps JSON strings made of an even number of
759759+ hexdecimal US-ASCII upper or lower case digits to the corresponding
760760+ byte sequence. On encoding uses only lower case hexadecimal
761761+ digits to encode the byte sequence. *)
762762+763763+(** {1:arrays Arrays and tuples}
764764+765765+ Read the {{!page-cookbook.dealing_with_arrays}cookbok} on arrays
766766+ and see also {{!array_queries}array queries and updates}. *)
767767+768768+(** Mapping JSON arrays. *)
769769+module Array : sig
770770+771771+ (** {1:maps Maps} *)
772772+773773+ type ('array, 'elt) enc =
774774+ { enc : 'acc. ('acc -> int -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc }
775775+ (** The type for specifying array encoding functions. A function to fold
776776+ over the elements of type ['elt] of the array of type ['array]. *)
777777+778778+ type ('array, 'elt, 'builder) map
779779+ (** The type for mapping JSON arrays with elements of type ['elt] to arrays
780780+ of type ['array] using values of type ['builder] to build them. *)
781781+782782+ val map :
783783+ ?kind:string -> ?doc:string ->
784784+ ?dec_empty:(unit -> 'builder) ->
785785+ ?dec_skip:(int -> 'builder -> bool) ->
786786+ ?dec_add:(int -> 'elt -> 'builder -> 'builder) ->
787787+ ?dec_finish:(Meta.t -> int -> 'builder -> 'array) ->
788788+ ?enc:('array, 'elt) enc ->
789789+ ?enc_meta:('array -> Meta.t) -> 'elt t ->
790790+ ('array, 'elt, 'builder) map
791791+ (** [map elt] maps JSON arrays of type ['elt] to arrays of
792792+ type ['array] built with type ['builder].
793793+ {ul
794794+ {- [kind] names the entities represented by the map and [doc]
795795+ documents them. Both default to [""].}
796796+ {- [dec_empty ()] is used to create a builder for the empty array.
797797+ Can be omitted if the map is only used for encoding, the default
798798+ unconditionally errors.}
799799+ {- [dec_skip i b] is used to skip the [i]th index of the JSON array.
800800+ If [true], the element is not decoded with [elt] and not added with
801801+ [dec_add] but skipped. The default always returns [false].}
802802+ {- [dec_add i v] is used to add the [i]th JSON element [v] $
803803+ decoded by [elt] to the builder [b]. Can be omitted if the map is
804804+ only used for encoding, the default unconditionally errors.}
805805+ {- [dec_finish b] converts the builder to the final array.
806806+ Can be omitted if the map is only used for encoding, the default
807807+ unconditionally errors.}
808808+ {- [enc.enc f acc a] folds over the elements of array [a] in
809809+ increasing order with [f] and starting with [acc]. This function
810810+ is used to encode [a] to a JSON array. Can be omitted if the
811811+ map is only used for decoding, the default unconditionally errors.}
812812+ {- [enc_meta a] is the metadata to use for encoding [v] to a JSON
813813+ array. Default returns {!Meta.none}.}} *)
814814+815815+ val list_map :
816816+ ?kind:string -> ?doc:string ->
817817+ ?dec_skip:(int -> 'a list -> bool) -> 'a t ->
818818+ ('a list, 'a, 'a list) map
819819+ (** [list_map elt] maps JSON arrays with elements of type [elt]
820820+ to [list] values. See also {!Jsont.list}. *)
821821+822822+ type 'a array_builder
823823+ (** The type for array builders. *)
824824+825825+ val array_map :
826826+ ?kind:string -> ?doc:string ->
827827+ ?dec_skip:(int -> 'a array_builder -> bool) -> 'a t ->
828828+ ('a array, 'a, 'a array_builder) map
829829+ (** [array_map elt] maps JSON arrays with elements of type [elt]
830830+ to [array] values. See also {!Jsont.array}. *)
831831+832832+ type ('a, 'b, 'c) bigarray_builder
833833+ (** The type for bigarray_builders. *)
834834+835835+ val bigarray_map :
836836+ ?kind:string -> ?doc:string ->
837837+ ?dec_skip:(int -> ('a, 'b, 'c) bigarray_builder -> bool) ->
838838+ ('a, 'b) Bigarray.kind -> 'c Bigarray.layout -> 'a t ->
839839+ (('a, 'b, 'c) Bigarray.Array1.t, 'a, ('a, 'b, 'c) bigarray_builder) map
840840+ (** [bigarray k l elt] maps JSON arrays with elements of
841841+ type [elt] to bigarray values of kind [k] and layout [l]. See
842842+ also {!Jsont.bigarray}. *)
843843+844844+ (** {1:types JSON types} *)
845845+846846+ val array : ('a, _, _) map -> 'a t
847847+ (** [array map] maps with [map] JSON arrays to values of type ['a].
848848+ See the the {{!section-arrays}array combinators}. *)
849849+850850+ val ignore : unit t
851851+ (** [ignore] ignores JSON arrays on decoding and errors on encoding. *)
852852+853853+ val zero : unit t
854854+ (** [zero] ignores JSON arrays on decoding and encodes an empty array. *)
855855+end
856856+857857+val list : ?kind:string -> ?doc:string -> 'a t -> 'a list t
858858+(** [list t] maps JSON arrays of type [t] to [list] values. See also
859859+ {!Array.list_map}. *)
860860+861861+val array : ?kind:string -> ?doc:string -> 'a t -> 'a array t
862862+(** [array t] maps JSON arrays of type [t] to [array] values. See
863863+ also {!Array.array_map}. *)
864864+865865+val array_as_string_map :
866866+ ?kind:string -> ?doc:string -> key:('a -> string) -> 'a t ->
867867+ 'a Map.Make(String).t t
868868+(** [array_as_string_map ~key t] maps JSON array elements of type [t] to
869869+ string maps by indexing them with [key]. If two elements have
870870+ the same [key] the element with the greatest index takes over.
871871+ Elements of the map are encoded to a JSON array in (binary) key order. *)
872872+873873+val bigarray :
874874+ ?kind:string -> ?doc:string -> ('a, 'b) Bigarray.kind -> 'a t ->
875875+ ('a, 'b, Bigarray.c_layout) Bigarray.Array1.t t
876876+(** [bigarray k t] maps JSON arrays of type [t] to [Bigarray.Array1.t] values.
877877+ See also {!Array.bigarray_map}. *)
878878+879879+val t2 :
880880+ ?kind:string -> ?doc:string -> ?dec:('a -> 'a -> 't2) ->
881881+ ?enc:('t2 -> int -> 'a) -> 'a t -> 't2 t
882882+(** [t2 ?dec ?enc t] maps JSON arrays with exactly 2 elements of type
883883+ [t] to value of type ['t2]. Decodes error if there are more
884884+ elements. [enc v i] must return the zero-based [i]th element. *)
885885+886886+val t3 :
887887+ ?kind:string -> ?doc:string -> ?dec:('a -> 'a -> 'a -> 't3) ->
888888+ ?enc:('t3 -> int -> 'a) -> 'a t -> 't3 t
889889+(** [t3] is like {!t2} but for 3 elements. *)
890890+891891+val t4 :
892892+ ?kind:string -> ?doc:string -> ?dec:('a -> 'a -> 'a -> 'a -> 't4) ->
893893+ ?enc:('t4 -> int -> 'a) -> 'a t -> 't4 t
894894+(** [t4] is like {!t2} but for 4 elements. *)
895895+896896+val tn : ?kind:string -> ?doc:string -> n:int -> 'a t -> 'a array t
897897+(** [tn ~n t] maps JSON arrays of exactly [n] elements of type [t] to
898898+ [array] values. This is {!val-array} limited by [n]. *)
899899+900900+(** {1:objects Objects}
901901+902902+ Read the {{!page-cookbook.dealing_with_objects}cookbook} on
903903+ objects. See a {{!page-cookbook.objects_as_records}simple
904904+ example}. See also {{!object_queries}object queries and
905905+ updates}. *)
906906+907907+(** Mapping JSON objects. *)
908908+module Object : sig
909909+910910+ (** {1:maps Maps} *)
911911+912912+ type ('o, 'dec) map
913913+ (** The type for mapping JSON objects to values of type ['o]. The
914914+ ['dec] type is used to construct ['o] from members see {!val-mem}. *)
915915+916916+ val map : ?kind:string -> ?doc:string -> 'dec -> ('o, 'dec) map
917917+ (** [map dec] is an empty JSON object decoded by function [dec].
918918+ {ul
919919+ {- [kind] names the entities represented by the map and [doc]
920920+ documents them. Both default to [""].}
921921+ {- [dec] is a constructor eventually returning a value of
922922+ type ['o] to be saturated with calls to {!val-mem}, {!val-case_mem}
923923+ or {!val-keep_unknown}. This is needed for decoding. Use {!enc_only}
924924+ if the result is only used for encoding.}} *)
925925+926926+ val map' :
927927+ ?kind:string -> ?doc:string -> ?enc_meta:('o -> Meta.t) ->
928928+ (Meta.t -> 'dec) -> ('o, 'dec) map
929929+ (** [map' dec] is like {!val-map} except you get the object's
930930+ decoding metdata in [dec] and [enc_meta] is used to recover it
931931+ on encoding. *)
932932+933933+ val enc_only :
934934+ ?kind:string -> ?doc:string -> ?enc_meta:('o -> Meta.t) -> unit ->
935935+ ('o, 'a) map
936936+ (** [enc_only ()] is like {!val-map'} but can only be used for
937937+ encoding. *)
938938+939939+ val finish : ('o, 'o) map -> 'o t
940940+ (** [finish map] is a JSON type for objects mapped by [map]. Raises
941941+ [Invalid_argument] if [map] describes a member name more than
942942+ once. *)
943943+944944+ (** {1:mems Members} *)
945945+946946+ (** Member maps.
947947+948948+ Usually it's better to use {!Jsont.Object.mem} or {!Jsont.Object.opt_mem}
949949+ directly. But this may be useful in certain abstraction contexts. *)
950950+ module Mem : sig
951951+952952+ type ('o, 'dec) object_map := ('o, 'dec) map
953953+954954+ type ('o, 'a) map
955955+ (** The type for mapping a member object to a value ['a] stored
956956+ in an OCaml value of type ['o]. *)
957957+958958+ val map :
959959+ ?doc:string -> ?dec_absent:'a -> ?enc:('o -> 'a) ->
960960+ ?enc_omit:('a -> bool) -> string -> 'a t -> ('o, 'a) map
961961+ (** See {!Jsont.Object.mem}. *)
962962+963963+ val app : ('o, 'a -> 'b) object_map -> ('o, 'a) map -> ('o, 'b) object_map
964964+ (** [app map mmap] applies the member map [mmap] to the contructor of
965965+ the object map [map]. In turn this adds the [mmap] member definition
966966+ to the object described by [map]. *)
967967+ end
968968+969969+ val mem :
970970+ ?doc:string -> ?dec_absent:'a -> ?enc:('o -> 'a) ->
971971+ ?enc_omit:('a -> bool) -> string -> 'a t -> ('o, 'a -> 'b) map ->
972972+ ('o, 'b) map
973973+ (** [mem name t map] is a member named [name] of type
974974+ [t] for an object of type ['o] being constructed by [map].
975975+ {ul
976976+ {- [doc] is a documentation string for the member. Defaults to [""].}
977977+ {- [dec_absent], if specified, is the value used for the decoding
978978+ direction when the member named [name] is missing. If unspecified
979979+ decoding errors when the member is absent. See also {!opt_mem}
980980+ and {{!page-cookbook.optional_members}this example}.}
981981+ {- [enc] is used to project the member's value from the object
982982+ representation ['o] for encoding to JSON with [t]. It can be omitted
983983+ if the result is only used for decoding.}
984984+ {- [enc_omit] is for the encoding direction. If the member value returned
985985+ by [enc] returns [true] on [enc_omit], the member is omited in the
986986+ encoded JSON object. Defaults to [Fun.const false].
987987+ See also {!opt_mem} and
988988+ {{!page-cookbook.optional_members}this example}.}} *)
989989+990990+ val opt_mem :
991991+ ?doc:string -> ?enc:('o -> 'a option) -> string -> 'a t ->
992992+ ('o, 'a option -> 'b) map -> ('o, 'b) map
993993+ (** [opt_mem name t map] is:
994994+ {[
995995+ let dec_absent = None and enc_omit = Option.is_none in
996996+ Jsont.Object.mem name (Jsont.some t) map ~dec_absent ~enc_omit
997997+ ]}
998998+ A shortcut to represent optional members of type ['a] with ['a option]
999999+ values. *)
10001000+10011001+ (** {1:cases Case objects}
10021002+10031003+ Read the {{!page-cookbook.cases}cookbook} on case objects. *)
10041004+10051005+ (** Case objects.
10061006+10071007+ Case objects are used to describe objects whose members depend
10081008+ on the tag value of a distinguished case member. See an
10091009+ {{!page-cookbook.cases}example}. *)
10101010+ module Case : sig
10111011+10121012+ (** {1:maps Maps} *)
10131013+10141014+ type 'a jsont := 'a t
10151015+10161016+ type ('cases, 'case, 'tag) map
10171017+ (** The type for mapping a case object represented by ['case] belonging to
10181018+ a common type represented by ['cases] depending on the value
10191019+ of a case member of type ['tag]. *)
10201020+10211021+ val map :
10221022+ ?dec:('case -> 'cases) -> 'tag -> 'case jsont ->
10231023+ ('cases, 'case, 'tag) map
10241024+ (** [map ~dec v obj] defines the object map [obj] as being the
10251025+ case for the tag value [v] of the case member. [dec] indicates how to
10261026+ inject the object case into the type common to all cases.
10271027+10281028+ Raises [Invalid_argument] if [obj] is not a direct result of
10291029+ {!finish}, that is if [obj] does not describe an object. *)
10301030+10311031+ val map_tag : ('cases, 'case, 'tag) map -> 'tag
10321032+ (** [map_tag m] is [m]'s tag. *)
10331033+10341034+ (** {1:cases Cases} *)
10351035+10361036+ type ('cases, 'tag) t
10371037+ (** The type for a case of the type ['cases]. This is
10381038+ {!type-map} with its ['case] representation hidden. *)
10391039+10401040+ val make : ('cases, 'case, 'tag) map -> ('cases, 'tag) t
10411041+ (** [make map] is [map] as a case. *)
10421042+10431043+ val tag : ('cases, 'tag) t -> 'tag
10441044+ (** [tag c] is the tag of [c]. *)
10451045+10461046+ (** {1:case Case values} *)
10471047+10481048+ type ('cases, 'tag) value
10491049+ (** The type for case values. This holds a case value and
10501050+ its case map {!type-map}. Use {!val-value} to construct them. *)
10511051+10521052+ val value : ('cases, 'case, 'tag) map -> 'case -> ('cases, 'tag) value
10531053+ (** [value map v] is a case value [v] described by [map]. *)
10541054+ end
10551055+10561056+ val case_mem :
10571057+ ?doc:string -> ?tag_compare:('tag -> 'tag -> int) ->
10581058+ ?tag_to_string:('tag -> string) -> ?dec_absent:'tag ->
10591059+ ?enc:('o -> 'cases) -> ?enc_omit:('tag -> bool) ->
10601060+ ?enc_case:('cases -> ('cases, 'tag) Case.value) -> string -> 'tag t ->
10611061+ ('cases, 'tag) Case.t list -> ('o, 'cases -> 'a) map -> ('o, 'a) map
10621062+ (** [case_mem name t cases map] is mostly like {!val-mem} except the member
10631063+ [name] selects an object representation according to the member value of
10641064+ type [t]:
10651065+ {ul
10661066+ {- [doc] is a documentation string for the member. Defaults to [""].}
10671067+ {- [tag_compare] is used to compare tags. Defaults to {!Stdlib.compare}}
10681068+ {- [tag_to_string] is used to stringify tags for improving
10691069+ error reporting.}
10701070+ {- [dec_absent], if specified, is the case value used for the decoding
10711071+ direction when the case member named [name] is missing. If unspecified
10721072+ decoding errors when the member is absent.}
10731073+ {- [enc] is used to project the value in which cases are stored
10741074+ from the object representation ['o] for encoding to JSON. It
10751075+ can be omitted if the result is only used for decoding.}
10761076+ {- [enc_case] determines the actual case value from the value returned
10771077+ by [enc].}
10781078+ {- [enc_omit] is used on the tag of the case returned by [enc_case]
10791079+ to determine if the case member can be ommited in the encoded JSON
10801080+ object}
10811081+ {- [cases] enumerates all the cases, it is needed for decoding.}}
10821082+10831083+ The names of the members of each case must be disjoint from [name]
10841084+ or those of [map] otherwise [Invalid_argument] is raised on
10851085+ {!finish}. Raises [Invalid_argument] if [case_mem] was already called
10861086+ on map. *)
10871087+10881088+ (** {1:unknown_members Unknown members}
10891089+10901090+ Read the {{!page-cookbook.unknown_members}cookbook} on unknown object
10911091+ members.
10921092+10931093+ On {{!cases}case objects} each individual case has its own
10941094+ behaviour unless the combinators are used on the case object map
10951095+ in which case it overrides the behaviour of cases. For those
10961096+ cases that use {!keep_unknown} they will get the result of an
10971097+ empty builder in their decoding function and the encoder is
10981098+ ignored on encode. *)
10991099+11001100+ (** Uniform members. *)
11011101+ module Mems : sig
11021102+11031103+ (** {1:maps Maps} *)
11041104+11051105+ type 'a jsont := 'a t
11061106+11071107+ type ('mems, 'a) enc =
11081108+ { enc :
11091109+ 'acc. (Meta.t -> string -> 'a -> 'acc -> 'acc) ->
11101110+ 'mems -> 'acc -> 'acc }
11111111+ (** The type for specifying unknown members encoding function.
11121112+ A function to fold over unknown members of uniform type ['a]
11131113+ stored in a value of type ['mems]. *)
11141114+11151115+ type ('mems, 'a, 'builder) map
11161116+ (** The type for mapping members of uniform type ['a] to values of
11171117+ type ['mems] using a builder of type ['builder]. *)
11181118+11191119+ val map :
11201120+ ?kind:string -> ?doc:string ->
11211121+ ?dec_empty:(unit -> 'builder) ->
11221122+ ?dec_add:(Meta.t -> string -> 'a -> 'builder -> 'builder) ->
11231123+ ?dec_finish:(Meta.t -> 'builder -> 'mems) ->
11241124+ ?enc:('mems, 'a) enc -> 'a jsont -> ('mems, 'a, 'builder) map
11251125+ (** [map type'] maps unknown members of uniform type ['a]
11261126+ to values of type ['mems] built with type ['builder].
11271127+ {ul
11281128+ {- [kind] names the entities represented by the map and [doc]
11291129+ documents them. Both default to [""].}
11301130+ {- [dec_empty] is used to create a builder for the members.
11311131+ Can be omitted if the map is only used for encoding, the default
11321132+ unconditionally errors.}
11331133+ {- [dec_add meta name v b] is used to add a member named [name]
11341134+ with meta [meta] with member value [v] to builder [b].
11351135+ Can be omitted if the map is only used for encoding, the default
11361136+ unconditionally errors.}
11371137+ {- [dec_finish meta b] converts the builder to the final members
11381138+ value. [meta] is the metadata of the object in which they were
11391139+ found. Can be omitted if the map is only used for encoding, the
11401140+ default unconditionally errors.}
11411141+ {- [enc f mems acc] folds over the elements of [mems] starting
11421142+ with [acc]. This function is used to encode the members.
11431143+ Can be omitted if the map is only used for decoding, the
11441144+ default unconditionally errors.}}
11451145+ See {!keep_unknown}. *)
11461146+11471147+ val string_map :
11481148+ ?kind:string -> ?doc:string -> 'a jsont ->
11491149+ ('a Stdlib.Map.Make(String).t, 'a, 'a Stdlib.Map.Make(String).t) map
11501150+ (** [string_map t] collects unknown member by name and types their
11511151+ values with [t]. See {!keep_unknown} and {!as_string_map}. *)
11521152+ end
11531153+11541154+ val skip_unknown : ('o, 'dec) map -> ('o, 'dec) map
11551155+ (** [skip_unknown map] makes [map] skip unknown members. This is the
11561156+ default, no need to specify it. Raises [Invalid_argument] if
11571157+ {!keep_unknown} was already specified on [map]. *)
11581158+11591159+ val error_unknown : ('o, 'dec) map -> ('o, 'dec) map
11601160+ (** [error_unknown map] makes [map] error on unknown members. Raises
11611161+ [Invalid_argument] if {!keep_unknown} was already specified on
11621162+ [map]. See {{!page-cookbook.erroring}this example}. *)
11631163+11641164+ val keep_unknown :
11651165+ ?enc:('o -> 'mems) -> ('mems, _, _) Mems.map ->
11661166+ ('o, 'mems -> 'a) map -> ('o, 'a) map
11671167+ (** [keep_unknown mems map] makes [map] keep unknown member with [mems].
11681168+ Raises [Invalid_argument] if {!keep_unknown} was already
11691169+ specified on [map]. See this {{!page-cookbook.keeping}this
11701170+ example}, {!Mems.string_map} and {!Jsont.json_mems}. *)
11711171+11721172+ (** {1:types JSON types } *)
11731173+11741174+ val as_string_map :
11751175+ ?kind:string -> ?doc:string -> 'a t -> 'a Stdlib.Map.Make(String).t t
11761176+ (** [as_string_map t] maps object to key-value maps of type [t].
11771177+ See also {!Mems.string_map} and {!Jsont.json_mems}. *)
11781178+11791179+ val zero : unit t
11801180+ (** [zero] ignores JSON objects on decoding and encodes an empty object. *)
11811181+end
11821182+11831183+(** {1:any Any} *)
11841184+11851185+val any :
11861186+ ?kind:string -> ?doc:string -> ?dec_null:'a t -> ?dec_bool:'a t ->
11871187+ ?dec_number:'a t -> ?dec_string:'a t -> ?dec_array:'a t ->
11881188+ ?dec_object:'a t -> ?enc:('a -> 'a t) -> unit -> 'a t
11891189+(** [any ()] maps subsets of JSON value of different sorts to values
11901190+ of type ['a]. The unspecified cases are not part of the subset and
11911191+ error on decoding. [enc] selects the type to use on encoding and errors
11921192+ if omitted. [kind] names the entities represented by the type and [doc]
11931193+ documents them, both defaults to [""]. *)
11941194+11951195+(** {1:maps Maps & recursion} *)
11961196+11971197+val map :
11981198+ ?kind:string -> ?doc:string -> ?dec:('a -> 'b) ->
11991199+ ?enc:('b -> 'a) -> 'a t -> 'b t
12001200+(** [map t] changes the type of [t] from ['a] to ['b].
12011201+ {ul
12021202+ {- [kind] names the entities represented by the type and [doc]
12031203+ documents them, both default to [""].}
12041204+ {- [dec] decodes values of type ['a] to values of type ['b].
12051205+ Can be omitted if the result is only used for
12061206+ encoding. The default errors.}
12071207+ {- [enc] encodes values of type ['b] to values of type ['a].
12081208+ Can be omitted if the result is only used for
12091209+ decoding. The default errors.}}
12101210+12111211+ For mapping base types use {!Jsont.Base.map}. *)
12121212+12131213+val iter :
12141214+ ?kind:string -> ?doc:string -> ?dec:('a -> unit) -> ?enc:('a -> unit) ->
12151215+ 'a t -> 'a t
12161216+(** [iter ?enc dec t] applies [dec] on decoding and [enc] on encoding
12171217+ but otherwise behaves like [t] does. Typically [dec] can be used
12181218+ to further assert the shape of the decoded value and {!Error.msgf}
12191219+ if it hasn't the right shape. [iter] can also be used as a tracing
12201220+ facility for debugging. *)
12211221+12221222+val rec' : 'a t Lazy.t -> 'a t
12231223+(** [rec'] maps recursive JSON values. See the {{!page-cookbook.recursion}
12241224+ cookbook}. *)
12251225+12261226+(** {1:ignoring Ignoring} *)
12271227+12281228+val ignore : unit t
12291229+(** [ignore] lossily maps all JSON values to [()] on decoding and
12301230+ errors on encoding. See also {!const}. *)
12311231+12321232+val zero : unit t
12331233+(** [zero] lossily maps all JSON values to [()] on decoding and
12341234+ encodes JSON nulls. *)
12351235+12361236+val todo : ?kind:string -> ?doc:string -> ?dec_stub:'a -> unit -> 'a t
12371237+(** [todo ?dec_stub ()] maps all JSON values to [dec_stub] if
12381238+ specified (errors otherwise) and errors on encoding. *)
12391239+12401240+(** {1:generic_json Generic JSON} *)
12411241+12421242+type name = string node
12431243+(** The type for JSON member names. *)
12441244+12451245+type mem = name * json
12461246+(** The type for generic JSON object members. *)
12471247+12481248+and object' = mem list
12491249+(** The type for generic JSON objects. *)
12501250+12511251+and json =
12521252+| Null of unit node
12531253+| Bool of bool node
12541254+| Number of float node
12551255+(** Encoders must use [Null] if float is {{!Float.is_finite}not finite}. *)
12561256+| String of string node
12571257+| Array of json list node
12581258+| Object of object' node (** *)
12591259+(** The type for generic JSON values. *)
12601260+12611261+(** Generic JSON values. *)
12621262+module Json : sig
12631263+12641264+ (** {1:json JSON values} *)
12651265+12661266+ type 'a jsont := 'a t
12671267+12681268+ type 'a cons = ?meta:Meta.t -> 'a -> json
12691269+ (** The type for constructing JSON values from an OCaml value of type ['a].
12701270+ [meta] defaults to {!Meta.none}. *)
12711271+12721272+ type t = json
12731273+ (** See {!Jsont.val-json}. *)
12741274+12751275+ val meta : json -> Meta.t
12761276+ (** [meta v] is the metadata of value [v]. *)
12771277+12781278+ val set_meta : Meta.t -> json -> json
12791279+ (** [set_meta m v] replaces [v]'s meta with [m]. *)
12801280+12811281+ val copy_layout : json -> dst:json -> json
12821282+ (** [copy_layout src ~dst] copies the layout of [src] and sets
12831283+ it on [dst] using {!Meta.copy_ws}. *)
12841284+12851285+ val sort : json -> Sort.t
12861286+ (** [sort v] is the sort of value [v]. *)
12871287+12881288+ val zero : json cons
12891289+ (** [zero j] is a stub value of the sort value of [j]. The stub
12901290+ value is the “natural” zero: null, false, 0, empty string,
12911291+ empty array, empty object. *)
12921292+12931293+ val equal : json -> json -> bool
12941294+ (** [equal j0 j1] is {!compare}[ j0 j1 = 0]. *)
12951295+12961296+ val compare : json -> json -> int
12971297+ (** [compare j0 j1] is a total order on JSON values:
12981298+ {ul
12991299+ {- Floating point values are compared with {!Float.compare},
13001300+ this means NaN values are equal.}
13011301+ {- Strings are compared byte wise.}
13021302+ {- Objects members are sorted before being compared.}
13031303+ {- {!Meta.t} values are ignored.}} *)
13041304+13051305+ val pp : t fmt
13061306+ (** See {!Jsont.pp_json}. *)
13071307+13081308+ (** {2:null Nulls and options} *)
13091309+13101310+ val null : unit cons
13111311+ (** [null] is [Null (unit, meta)]. *)
13121312+13131313+ val option : 'a cons -> 'a option cons
13141314+ (** [option c] constructs [Some v] values with [c v] and [None] ones
13151315+ with {!val-null}. *)
13161316+13171317+ (** {2:bool Booleans} *)
13181318+13191319+ val bool : bool cons
13201320+ (** [bool b] is [Bool (b, meta)]. *)
13211321+13221322+ (** {2:numbers Numbers} *)
13231323+13241324+ val number : float cons
13251325+ (** [number n] is [Number (n, meta)]. *)
13261326+13271327+ val any_float : float cons
13281328+ (** [any_float v] is [number v] if {!Float.is_finite}[ v] is [true]
13291329+ and [string (Float.to_string v)] otherwise. See {!Jsont.any_float}. *)
13301330+13311331+ val int32 : int32 cons
13321332+ (** [int32] is [i] as a JSON number. *)
13331333+13341334+ val int64 : int64 cons
13351335+ (** [int64 i] is [i] as a JSON number or a JSON string if
13361336+ not in the range \[-2{^53};2{^53}\]. See also {!int64_as_string}. *)
13371337+13381338+ val int64_as_string : int64 cons
13391339+ (** [int64_as_string i] is [i] as a JSON string. See also {!int64}. *)
13401340+13411341+ val int : int cons
13421342+ (** [int] is [i] as a JSON number or a JSON string if not
13431343+ in the range \[-2{^53};2{^53}\]. See also {!int_as_string}. *)
13441344+13451345+ val int_as_string : int cons
13461346+ (** [int_as_string i] is [i] as a JSON string. See also {!int}. *)
13471347+13481348+ (** {2:strings Strings} *)
13491349+13501350+ val string : string cons
13511351+ (** [string s] is [String (s, meta)]. *)
13521352+13531353+ (** {2:arrays Arrays} *)
13541354+13551355+ val list : json list cons
13561356+ (** [list l] is [Array (l, meta)]. *)
13571357+13581358+ val array : json array cons
13591359+ (** [array l] is [Array (Array.to_list a, meta)]. See also {!list}. *)
13601360+13611361+ (** {2:objects Objects} *)
13621362+13631363+ val name : ?meta:Meta.t -> string -> name
13641364+ (** [name ?meta n] is [(n, meta)]. [meta] defaults to {!Meta.none}. *)
13651365+13661366+ val mem : name -> json -> mem
13671367+ (** [mem n v] is [(n, v)]. [meta] defaults to {!Meta.none}. *)
13681368+13691369+ val object' : object' cons
13701370+ (** [object o] is [Object (o, meta)]. *)
13711371+13721372+ val find_mem : string -> object' -> mem option
13731373+ (** [find_mem n ms] find the first member whose name matches [n] in [ms]. *)
13741374+13751375+ val find_mem' : name -> object' -> mem option
13761376+ (** [find_mem n ms] is [find_mem (fst n) ms]. *)
13771377+13781378+ val object_names : object' -> string list
13791379+ (** [object_names ms] are the names of [ms]. *)
13801380+13811381+ val object_names' : object' -> name list
13821382+ (** [object_names ms] are the names of [ms]. *)
13831383+13841384+ (** {1:decode Decode} *)
13851385+13861386+ val decode : 'a jsont -> json -> ('a, string) result
13871387+ (** [decode t j] decodes a value from the generic JSON [j] according
13881388+ to type [t]. *)
13891389+13901390+ val decode' : 'a jsont -> json -> ('a, Error.t) result
13911391+ (** [decode'] is like {!val-decode} but preserves the error structure. *)
13921392+13931393+ (** {1:encode Encode} *)
13941394+13951395+ val encode : 'a jsont -> 'a -> (json, string) result
13961396+ (** [encode t v] encodes a generic JSON value for [v] according
13971397+ to type [t]. *)
13981398+13991399+ val encode' : 'a jsont -> 'a -> (json, Error.t) result
14001400+ (** [encode'] is like {!val-encode} but preserves the error structure. *)
14011401+14021402+ (** {1:recode Recode} *)
14031403+14041404+ val recode : 'a jsont -> json -> (json, string) result
14051405+ (** [recode t v] decodes [v] with [t] and encodes it with [t]. *)
14061406+14071407+ val recode' : 'a jsont -> json -> (json, Error.t) result
14081408+ (** [recode'] is like {!val-recode} but preserves the error structure. *)
14091409+14101410+ val update : 'a jsont -> json -> json
14111411+ (** [update] is like {!val-recode} but raises {!Jsont.exception-Error}. *)
14121412+14131413+ (** {1:errors Errors} *)
14141414+14151415+ val error_sort : exp:Sort.t -> json -> 'a
14161416+ (** [error_sort ~exp fnd] errors when sort [exp] was expected but
14171417+ generic JSON [fnd] was found. *)
14181418+14191419+ val error_type : 'a jsont -> json -> 'a
14201420+ (** [error_type t fnd] errors when the type expected by [t]
14211421+ does not match [fnd]. *)
14221422+end
14231423+14241424+val json : json t
14251425+(** [json] maps any JSON value to its generic representation. *)
14261426+14271427+val json_null : json t
14281428+(** [json_null] maps JSON nulls to their generic representation. *)
14291429+14301430+val json_bool : json t
14311431+(** [json_bool] maps JSON booleans to their generic representation. *)
14321432+14331433+val json_number : json t
14341434+(** [json_number] maps JSON nulls or numbers
14351435+ ({{!page-cookbook.non_finite_numbers}explanation}) to their generic
14361436+ representation. *)
14371437+14381438+val json_string : json t
14391439+(** [json_string] represents JSON strings by their generic representation. *)
14401440+14411441+val json_array : json t
14421442+(** [json_array] represents JSON arrays by their generic representation. *)
14431443+14441444+val json_object : json t
14451445+(** [json_object] represents JSON objects by their generic representation. *)
14461446+14471447+val json_mems : (json, json, mem list) Object.Mems.map
14481448+(** [json_mems] is a members map collecting unknown members into a
14491449+ generic JSON object. See {{!page-cookbook.keeping}this example}. *)
14501450+14511451+(** {1:queries Queries and updates}
14521452+14531453+ Queries are lossy or aggregating decodes. Updates decode to
14541454+ {!type-json} values but transform the data along the way. They allow to
14551455+ process JSON data without having to fully model it
14561456+ (see the update example in the {{!page-index.quick_start}quick start}). *)
14571457+14581458+val const : 'a t -> 'a -> 'a t
14591459+(** [const t v] maps any JSON value to [v] on decodes and
14601460+ unconditionally encodes [v] with [t]. *)
14611461+14621462+val recode : dec:'a t -> ('a -> 'b) -> enc:'b t -> 'b t
14631463+(** [recode ~dec f ~enc] maps on decodes like [dec] does followed by
14641464+ [f] and on encodes uses [enc]. This can be used to change the JSON
14651465+ sort of value. For example:
14661466+{[
14671467+recode ~dec:int (fun _ i -> string_of_int s) ~enc:string
14681468+]}
14691469+ decodes an integer but encodes the integer as a string. *)
14701470+14711471+val update : 'a t -> json t
14721472+(** [update t] decodes any JSON with [t] and directly encodes it back
14731473+ with [t] to yield the decode result. Encodes any JSON like {!val-json}
14741474+ does. *)
14751475+14761476+(** {2:array_queries Arrays} *)
14771477+14781478+val nth : ?absent:'a -> int -> 'a t -> 'a t
14791479+(** [nth n t] decodes the [n]th index of a JSON array with [t]. Other
14801480+ indices are skipped. The decode errors if there is no such index
14811481+ unless [absent] is specified in which case this value is returned.
14821482+ Encodes a singleton array. *)
14831483+14841484+val set_nth : ?stub:json -> ?allow_absent:bool -> 'a t -> int -> 'a -> json t
14851485+(** [set_nth t n v] on decodes sets the [n]th value of a JSON array to
14861486+ [v] encoded by [t]. Other indices are left untouched. Errors if
14871487+ there is no such index unless [~allow_absent:true] is specified in
14881488+ which case the index is created preceeded by as many [stub]
14891489+ indices as needed. [stub] defaults to {!Json.zero} applied to the
14901490+ value [v] encoded by [t] (i.e. the "natural zero" of [v]'s encoding sort).
14911491+ Encodes like {!json_array} does. *)
14921492+14931493+val update_nth : ?stub:json -> ?absent:'a -> int -> 'a t -> json t
14941494+(** [update_nth n t] on decode recodes the [n]th value of a JSON array
14951495+ with [t]. Errors if there is no such index unless [absent] is
14961496+ specified in which case the index is created with [absent],
14971497+ encoded with [t] and preceeded by as many [stub] values as
14981498+ needed. [stub] defaults to {!Json.zero} applied to the recode.
14991499+ Encodes like {!json_array} does. *)
15001500+15011501+val delete_nth : ?allow_absent:bool -> int -> json t
15021502+(** [delete_nth n] drops the [n]th index of a JSON array on both
15031503+ decode and encodes. Other indices are left untouched. Errors if
15041504+ there is no such index unless [~allow_absent:true] is specified in
15051505+ which case the data is left untouched. *)
15061506+15071507+val filter_map_array : 'a t -> 'b t -> (int -> 'a -> 'b option) -> json t
15081508+(** [filter_map_array a b f] maps the [a] elements of a JSON array
15091509+ with [f] to [b] elements or deletes them on [None]. Encodes
15101510+ generic JSON arrays like {!json_array} does. *)
15111511+15121512+val fold_array : 'a t -> (int -> 'a -> 'b -> 'b) -> 'b -> 'b t
15131513+(** [fold_array t f acc] fold [f] over the [t] elements of a JSON
15141514+ array starting with [acc]. Encodes an empty JSON array. *)
15151515+15161516+(** {2:object_queries Objects} *)
15171517+15181518+val mem : ?absent:'a -> string -> 'a t -> 'a t
15191519+(** [mem name t] decodes the member named [name] of a JSON object with
15201520+ [t]. Other members are skipped. The decode errors if there is no
15211521+ such member unless [absent] is specified in which case this value
15221522+ is returned. Encodes an object with a single [name] member. *)
15231523+15241524+val set_mem : ?allow_absent:bool -> 'a t -> string -> 'a -> json t
15251525+(** [set_mem t name v] sets the member value of [name] of a [JSON]
15261526+ object to an encoding of [v] with [t]. This happens both on
15271527+ decodes and encodes. Errors if there is no such member unless
15281528+ [allow_absent:true] is specified in which case a member is added
15291529+ to the object. *)
15301530+15311531+val update_mem : ?absent:'a -> string -> 'a t -> json t
15321532+(** [update_mem name t] recodes the member value of [name] of a JSON
15331533+ object with [t]. This happens both on decodes and encodes. Errors
15341534+ if there is no such member unless [absent] is specified in which
15351535+ case a member with this value encoded with [t] is added to the
15361536+ object. *)
15371537+15381538+val delete_mem : ?allow_absent:bool -> string -> json t
15391539+(** [delete_mem name] deletes the member named [name] of a JSON object
15401540+ on decode. Other members are left untouched. The decode errors if
15411541+ there is no such member unless [~allow_absent:true] is specified
15421542+ in which case the data is left untouched. Encodes generic JSON
15431543+ objects like {!json_object} does. *)
15441544+15451545+val filter_map_object :
15461546+ 'a t -> 'b t -> (Meta.t -> string -> 'a -> (name * 'b) option) -> json t
15471547+(** [filter_map_object a b f] maps the [a] members of a JSON object
15481548+ with [f] to [(n, b)] members or deletes them on [None]. The meta
15491549+ given to [f] is the meta of the member name. Encodes generic JSON
15501550+ arrays like {!json_object} does. *)
15511551+15521552+val fold_object : 'a t -> (Meta.t -> string -> 'a -> 'b -> 'b) -> 'b -> 'b t
15531553+(** [fold_object t f acc] folds [f] over the [t] members of a JSON object
15541554+ starting with [acc]. Encodes an empty JSON object. *)
15551555+15561556+(** {2:index_queries Indices} *)
15571557+15581558+val index : ?absent:'a -> Path.index -> 'a t -> 'a t
15591559+(** [index] uses {!val-nth} or {!val-mem} on the given index. *)
15601560+15611561+val set_index : ?allow_absent:bool -> 'a t -> Path.index -> 'a -> json t
15621562+(** [set_index] uses {!set_nth} or {!set_mem} on the given index. *)
15631563+15641564+val update_index : ?stub:json -> ?absent:'a -> Path.index -> 'a t -> json t
15651565+(** [update_index] uses {!update_nth} or {!update_mem} on the given index. *)
15661566+15671567+val delete_index : ?allow_absent:bool -> Path.index -> json t
15681568+(** [delete_index] uses {!delete_nth} or {!delete_mem} on the given index. *)
15691569+15701570+(** {2:path_queries Paths} *)
15711571+15721572+val path : ?absent:'a -> Path.t -> 'a t -> 'a t
15731573+(** [path p t] {{!index}decodes} with [t] on the last index of [p]. If
15741574+ [p] is {!Path.root} this is [t]. *)
15751575+15761576+val set_path :
15771577+ ?stub:json -> ?allow_absent:bool -> 'a t -> Path.t -> 'a -> json t
15781578+(** [set_path t p v] {{!set_index}sets} the last index of [p]. If [p]
15791579+ is {!Path.root} this encodes [v] with [t]. *)
15801580+15811581+val update_path : ?stub:json -> ?absent:'a -> Path.t -> 'a t -> json t
15821582+(** [update_path p t] {{!update_index}updates} the last index of [p] with
15831583+ [t]. On the root path this is [t]. *)
15841584+15851585+val delete_path : ?allow_absent:bool -> Path.t -> json t
15861586+(** [delete_path p] {{!delete_index}deletes} the last index of [p]. If
15871587+ [p] is {!Path.root} this is {!Json.val-null}. *)
15881588+15891589+(** {1:fmt Formatting} *)
15901590+15911591+type format =
15921592+| Minify (** Compact. No whitespace, no newlines. *)
15931593+| Indent (** Indented output (not necessarily pretty). *)
15941594+| Layout (** Follow {!Meta} layout information. *)
15951595+(** The type for specifying JSON encoding formatting. See for example
15961596+ {!Jsont_bytesrw.val-encode}. *)
15971597+15981598+type number_format = (float -> unit, Format.formatter, unit) Stdlib.format
15991599+(** The type for JSON number formatters. *)
16001600+16011601+val default_number_format : number_format
16021602+(** [default_number_format] is ["%.17g"]. This number formats ensures
16031603+ that finite floating point values can be interchanged without loss
16041604+ of precision. *)
16051605+16061606+val pp_null : unit fmt
16071607+(** [pp_null] formats a JSON null. *)
16081608+16091609+val pp_bool : bool fmt
16101610+(** [pp_bool] formats a JSON bool. *)
16111611+16121612+val pp_number : float fmt
16131613+(** [pp_number] formats a JSON number of a JSON null if the float
16141614+ is not finite. Uses the {!default_number_format}. *)
16151615+16161616+val pp_number' : number_format -> float fmt
16171617+(** [pp_number fmt] is like {!pp_number} but uses [fmt] to format the
16181618+ number. *)
16191619+16201620+val pp_string : string fmt
16211621+(** [pp_string] formats a JSON string (quoted and escaped). Assumes
16221622+ the string is valid UTF-8. *)
16231623+16241624+val pp_json : json fmt
16251625+(** [pp_json] formats JSON, see {!pp_json'}. *)
16261626+16271627+val pp_json' : ?number_format:number_format -> unit -> json fmt
16281628+(** [pp' ~format ~number_format () ppf j] formats [j] on [ppf]. The output
16291629+ is indented but may be more compact than an [Indent] JSON encoder may do.
16301630+ For example arrays may be output on one line if they fit etc.
16311631+ {ul
16321632+ {- [number_format] is used to format JSON numbers. Defaults to
16331633+ {!default_number_format}}
16341634+ {- Non-finite numbers are output as JSON nulls
16351635+ ({{!page-cookbook.non_finite_numbers}explanation}).}
16361636+ {- Strings are assumed to be valid UTF-8.}} *)
16371637+16381638+val pp_value : ?number_format:number_format -> 'a t -> unit -> 'a fmt
16391639+(** [pp_value t ()] formats the JSON representation of values as
16401640+ described by [t] by encoding it with {!Json.val-encode} and formatting
16411641+ it with {!pp_json'}. If the encoding of the value errors a JSON
16421642+ string with the error message is formatted. This means that {!pp_value}
16431643+ should always format valid JSON text. *)
16441644+16451645+(** {1:low Low-level representation} *)
16461646+16471647+(** Low level representation (unstable).
16481648+16491649+ This representation may change even between minor versions of the
16501650+ library. It can be used to devise new processors on JSON types.
16511651+16521652+ Processors should be ready to catch the {!Jsont.exception-Error} exception
16531653+ when they invoke functional members of the representation.
16541654+16551655+ Processors should make sure they interpret mappings
16561656+ correctly. In particular:
16571657+ {ul
16581658+ {- The [Number] case represents the sets of JSON numbers and nulls.}}
16591659+16601660+ See the source of {!Json.decode'} and {!Json.encode'}
16611661+ for a simple example on how to process this representation. The
16621662+ {{:https://erratique.ch/repos/jsont/tree/paper}paper}
16631663+ in the Jsont source repository may also help to understand this menagerie
16641664+ of types. *)
16651665+module Repr : sig
16661666+ type 'a t' := 'a t
16671667+16681668+ module String_map : Map.S with type key = string
16691669+ (** A [Map.Make(String)] instance. *)
16701670+16711671+ (** Type identifiers. Can be removed once we require OCaml 5.1 *)
16721672+ module Type : sig
16731673+ type (_, _) eq = Equal : ('a, 'a) eq
16741674+ module Id : sig
16751675+ type 'a t
16761676+ val make : unit -> 'a t
16771677+ val uid : 'a t -> int
16781678+ val provably_equal : 'a t -> 'b t -> ('a, 'b) eq option
16791679+ end
16801680+ end
16811681+16821682+ type ('ret, 'f) dec_fun =
16831683+ | Dec_fun : 'f -> ('ret, 'f) dec_fun
16841684+ (** The function and its return type. *)
16851685+ | Dec_app : ('ret, 'a -> 'b) dec_fun * 'a Type.Id.t -> ('ret, 'b) dec_fun
16861686+ (** Application of an argument to a function witnessed by a type
16871687+ identifier. The type identifier can be used to lookup a value
16881688+ of the right type in an heterogenous dictionary. *)
16891689+ (** The type for decoding functions. *)
16901690+16911691+ (** {1:base Base value maps} *)
16921692+16931693+ type ('a, 'b) base_map =
16941694+ { kind : string;
16951695+ (** The kind of JSON value that are mapped (documentation) *)
16961696+ doc : string;
16971697+ (** A doc string for the kind of JSON value. *)
16981698+ dec : Meta.t -> 'a -> 'b;
16991699+ (** [dec] decodes a base value represented by its metadata and ['a] to
17001700+ ['b]. *)
17011701+ enc : 'b -> 'a;
17021702+ (** [enc] encodes a value of type ['b] to a base JSON value represented
17031703+ by ['a]. *)
17041704+ enc_meta : 'b -> Meta.t;
17051705+ (** [enc_meta] recovers the base JSON value metadata from ['b] (if any). *)
17061706+ }
17071707+ (** The type for mapping JSON base values represented in OCaml by
17081708+ ['a] (these values are fixed by the cases in {!t}) to a value of
17091709+ type ['b]. *)
17101710+17111711+ (** {1:types JSON types} *)
17121712+17131713+ type 'a t =
17141714+ | Null : (unit, 'a) base_map -> 'a t (** Null maps. *)
17151715+ | Bool : (bool, 'a) base_map -> 'a t (** Boolean maps. *)
17161716+ | Number : (float, 'a) base_map -> 'a t (** Number maps. *)
17171717+ | String : (string, 'a) base_map -> 'a t (** String maps. *)
17181718+ | Array : ('a, 'elt, 'builder) array_map -> 'a t (** Array maps. *)
17191719+ | Object : ('o, 'o) object_map -> 'o t (** Object maps. *)
17201720+ | Any : 'a any_map -> 'a t (** Map for different sorts of JSON values. *)
17211721+ | Map : ('b, 'a) map -> 'a t (** Map from JSON type ['b] to JSON type ['a]. *)
17221722+ | Rec : 'a t Lazy.t -> 'a t (** Recursive definition. *)
17231723+ (** The type for JSON types. *)
17241724+17251725+ (** {1:array Array maps} *)
17261726+17271727+ and ('array, 'elt, 'builder) array_map =
17281728+ { kind : string;
17291729+ (** The kind of JSON array mapped (documentation). *)
17301730+ doc : string;
17311731+ (** Documentation string for the JSON array. *)
17321732+ elt : 'elt t;
17331733+ (** The type for the array elements. *)
17341734+ dec_empty : unit -> 'builder;
17351735+ (** [dec_empty ()] creates a new empty array builder. *)
17361736+ dec_skip : int -> 'builder -> bool;
17371737+ (** [dec_skip i b] determines if the [i]th index of the JSON array can be
17381738+ skipped. *)
17391739+ dec_add : int -> 'elt -> 'builder -> 'builder;
17401740+ (** [dec_add] adds the [i]th index value of the JSON array
17411741+ as decoded by [elt] to the builder. *)
17421742+ dec_finish : Meta.t -> int -> 'builder -> 'array;
17431743+ (** [dec_finish] turns the builder into an array given its
17441744+ metadata and length. *)
17451745+ enc : 'acc. ('acc -> int -> 'elt -> 'acc) -> 'acc -> 'array -> 'acc;
17461746+ (** [enc] folds over the elements of the array for encoding. *)
17471747+ enc_meta : 'array -> Meta.t;
17481748+ (** [enc_meta] recovers the metadata of an array (if any). *) }
17491749+ (** The type for mapping JSON arrays to values of type ['array]
17501750+ with array elements mapped to type ['elt] and using a ['builder]
17511751+ value to construct the array. *)
17521752+17531753+ (** {1:object_map Object maps} *)
17541754+17551755+ and ('o, 'dec) object_map =
17561756+ { kind : string;
17571757+ (** The kind of JSON object (documentation). *)
17581758+ doc : string;
17591759+ (** A doc string for the JSON member. *)
17601760+ dec : ('o, 'dec) dec_fun;
17611761+ (** The object decoding function to construct an ['o] value. *)
17621762+ mem_decs : mem_dec String_map.t;
17631763+ (** [mem_decs] are the member decoders sorted by member name. *)
17641764+ mem_encs : 'o mem_enc list;
17651765+ (** [mem_encs] is the list of member encoders. *)
17661766+ enc_meta : 'o -> Meta.t;
17671767+ (** [enc_meta] recovers the metadata of an object (if any). *)
17681768+ shape : 'o object_shape;
17691769+ (** [shape] is the {{!object_shape}shape} of the object. *) }
17701770+ (** The type for mapping a JSON object to values of type ['o] using
17711771+ a decoding function of type ['dec]. [mem_decs] and [mem_encs]
17721772+ have the same {!mem_map} values they are just sorted
17731773+ differently for decoding and encoding purposes. *)
17741774+17751775+ and mem_dec = Mem_dec : ('o, 'a) mem_map -> mem_dec
17761776+ (** The type for member maps in decoding position. *)
17771777+17781778+ and 'o mem_enc = Mem_enc : ('o, 'a) mem_map -> 'o mem_enc
17791779+ (** The type for member maps in encoding position. *)
17801780+17811781+ and ('o, 'a) mem_map =
17821782+ { name : string;
17831783+ (** The JSON member name. *)
17841784+ doc : string;
17851785+ (** Documentation for the JSON member. *)
17861786+ type' : 'a t;
17871787+ (** The type for the member value. *)
17881788+ id : 'a Type.Id.t;
17891789+ (** A type identifier for the member. This allows to store
17901790+ the decode in a {!Dict.t} on decode and give it in time
17911791+ to the object decoding function of the object map. *)
17921792+ dec_absent : 'a option;
17931793+ (** The value to use if absent (if any). *)
17941794+ enc : 'o -> 'a;
17951795+ (** [enc] recovers the value to encode from ['o]. *)
17961796+ (* enc_name_meta : 'a -> Meta.t;
17971797+ XXX This should have been the meta found for the name, but
17981798+ that does not fit so well in the member combinators, it's
17991799+ not impossible to fit it in but likely increases the cost
18001800+ for decoding objects. The layout preserving updates occur
18011801+ via generic JSON which uses [mems_map] in which the meta
18021802+ is available in [dec_add]. Let's leave it that way for now. *)
18031803+ enc_omit : 'a -> bool;
18041804+ (** [enc_omit] is [true] if the result of [enc] should
18051805+ not be encoded. *)
18061806+ }
18071807+ (** The type for mapping a JSON member to a value of type ['a] in
18081808+ an object represented by a value of type ['o]. *)
18091809+18101810+ and 'o object_shape =
18111811+ | Object_basic : ('o, 'mems, 'builder) unknown_mems -> 'o object_shape
18121812+ (** A basic object, possibly indicating how to handle unknown members *)
18131813+ | Object_cases :
18141814+ ('o, 'mems, 'builder) unknown_mems option *
18151815+ ('o, 'cases, 'tag) object_cases -> 'o object_shape
18161816+ (** An object with a case member each case further describing
18171817+ an object map. *)
18181818+ (** The type for object shapes. *)
18191819+18201820+ (** {2:unknown_mems Unknown members} *)
18211821+18221822+ and ('o, 'mems, 'builder) unknown_mems =
18231823+ | Unknown_skip : ('o, unit, unit) unknown_mems
18241824+ (** Skip unknown members. *)
18251825+ | Unknown_error : ('o, unit, unit) unknown_mems
18261826+ (** Error on unknown members. *)
18271827+ | Unknown_keep :
18281828+ ('mems, 'a, 'builder) mems_map * ('o -> 'mems) ->
18291829+ ('o, 'mems, 'builder) unknown_mems
18301830+ (** Gather unknown members in a member map. *)
18311831+ (** The type for specifying decoding behaviour on unknown JSON object
18321832+ members. *)
18331833+18341834+ and ('mems, 'a, 'builder) mems_map =
18351835+ { kind : string; (** The kind for unknown members (documentation). *)
18361836+ doc : string; (** Documentation string for the unknown members. *)
18371837+ mems_type : 'a t; (** The uniform type according which unknown members
18381838+ are typed. *)
18391839+ id : 'mems Type.Id.t; (** A type identifier for the unknown member
18401840+ map. *)
18411841+ dec_empty : unit -> 'builder;
18421842+ (** [dec_empty] create a new empty member map builder. *)
18431843+ dec_add : Meta.t -> string -> 'a -> 'builder -> 'builder;
18441844+ (** [dec_add] adds a member named [n] with metadata [meta] and
18451845+ value parsed by [mems_type] to the builder. *)
18461846+ dec_finish : Meta.t -> 'builder -> 'mems;
18471847+ (** [dec_finish] turns the builder into an unknown member map.
18481848+ The [meta] is the meta data of the object in which they were found. *)
18491849+ enc :
18501850+ 'acc. (Meta.t -> string -> 'a -> 'acc -> 'acc) -> 'mems -> 'acc -> 'acc;
18511851+ (** [enc] folds over the member map for encoding. *)
18521852+ }
18531853+ (** The type for gathering unknown JSON members uniformly typed
18541854+ according to ['a] in a map ['mems] constructed with ['builder]. *)
18551855+18561856+ (** {2:case_objects Case objects} *)
18571857+18581858+ and ('o, 'cases, 'tag) object_cases =
18591859+ { tag : ('tag, 'tag) mem_map;
18601860+ (** The JSON member used to decide cases. The [enc] field of
18611861+ this [mem_map] should be the identity, this allows
18621862+ encoders to reuse generic encoding code for members. We
18631863+ don't have [('o, 'tag) mem_map] here because the tag is not
18641864+ stored we recover the case via [enc] and [enc_case] below. *)
18651865+ tag_compare : 'tag -> 'tag -> int;
18661866+ (** The function to compare tags. *)
18671867+ tag_to_string : ('tag -> string) option;
18681868+ (** The function to stringify tags for error reporting. *)
18691869+ id : 'cases Type.Id.t;
18701870+ (** A type identifier for the tag. *)
18711871+ cases : ('cases, 'tag) case list;
18721872+ (** The list of possible cases. *)
18731873+ enc : 'o -> 'cases;
18741874+ (** [enc] is the function to recover case values from the value
18751875+ ['o] the object is mapped to. *)
18761876+ enc_case : 'cases -> ('cases, 'tag) case_value;
18771877+ (** [enc_case] retrieves the concrete case from the common
18781878+ [cases] values. You can see it as preforming a match. *)
18791879+ }
18801880+ (** The type for object cases mapped to a common type ['cases] stored
18811881+ in a vlue of type ['o] and identified by tag values of type ['tag]. *)
18821882+18831883+ and ('cases, 'case, 'tag) case_map =
18841884+ { tag : 'tag;
18851885+ (** The tag value for the case. *)
18861886+ object_map : ('case, 'case) object_map;
18871887+ (** The object map for the case. *)
18881888+ dec : 'case -> 'cases;
18891889+ (** [dec] is the function used on decoding to inject the case
18901890+ into the common ['cases] type. *)
18911891+ }
18921892+ (** The type for an object case with common type ['cases] specific
18931893+ type ['case] and tag type ['tag]. *)
18941894+18951895+ and ('cases, 'tag) case_value =
18961896+ | Case_value :
18971897+ ('cases, 'case, 'tag) case_map * 'case -> ('cases, 'tag) case_value
18981898+ (** The type for case values. This packs a case value and its
18991899+ description. *)
19001900+19011901+ and ('cases, 'tag) case =
19021902+ | Case : ('cases, 'case, 'tag) case_map -> ('cases, 'tag) case
19031903+ (** The type for hiding the the concrete type of a case . *)
19041904+19051905+ (** {1:any Any maps} *)
19061906+19071907+ and 'a any_map =
19081908+ { kind : string;
19091909+ (** The kind of JSON values mapped (documentation). *)
19101910+ doc : string;
19111911+ (** Documentation string for the kind of values. *)
19121912+ dec_null : 'a t option;
19131913+ (** [dec_null], if any, is used for decoding JSON nulls. *)
19141914+ dec_bool : 'a t option;
19151915+ (** [dec_bool], if any, is used for decoding JSON bools. *)
19161916+ dec_number : 'a t option;
19171917+ (** [dec_number], if any, is used for decoding JSON numbers. *)
19181918+ dec_string : 'a t option;
19191919+ (** [dec_string], if any, is used for decoding JSON strings. *)
19201920+ dec_array : 'a t option;
19211921+ (** [dec_array], if any, is used for decoding JSON arrays. *)
19221922+ dec_object : 'a t option;
19231923+ (** [dec_object], if any, is used for decoding JSON objects. *)
19241924+ enc : 'a -> 'a t;
19251925+ (** [enc] specifies the encoder to use on a given value. *)
19261926+ }
19271927+ (** The type for mapping JSON values with multiple sorts to a value
19281928+ of type ['a]. If a decoding case is [None], the decoding
19291929+ errors on these JSON values. *)
19301930+19311931+ (** {1:type_map Type maps} *)
19321932+19331933+ and ('a, 'b) map =
19341934+ { kind : string;
19351935+ (** The kind of JSON values mapped (documentation). *)
19361936+ doc : string;
19371937+ (** Documentation string for the kind of values. *)
19381938+ dom : 'a t;
19391939+ (** The domain of the map. *)
19401940+ dec : 'a -> 'b;
19411941+ (** [dec] decodes ['a] to ['b]. *)
19421942+ enc : 'b -> 'a;
19431943+ (** [enc] encodes ['b] to ['a]. *) }
19441944+ (** The type for mapping JSON types of type ['a] to a JSON type of
19451945+ type ['b]. *)
19461946+19471947+ (** {1:conv Convert} *)
19481948+19491949+ val of_t : 'a t' -> 'a t
19501950+ (** [of_t] is {!Stdlib.Fun.id}. *)
19511951+19521952+ val unsafe_to_t : 'a t -> 'a t'
19531953+ (** [unsafe_to_t r] converts the representation to a type [r]. It
19541954+ is unsafe because constructors of the {!Jsont} module do
19551955+ maintain some invariants. *)
19561956+19571957+ (** {1:kinds Kinds and doc} *)
19581958+19591959+ val kinded_sort : 'a t -> string
19601960+ (** [kinded_sort t] is kinded sort of [t], see {!Jsont.kinded_sort}. *)
19611961+19621962+ val array_map_kinded_sort : ('a, 'elt, 'builder) array_map -> string
19631963+ (** [array_map_kinded_sort map] is like {!kinded_sort} but
19641964+ acts directly on the array [map]. *)
19651965+19661966+ val object_map_kinded_sort : ('o, 'dec) object_map -> string
19671967+ (** [object_map_kind map] is like {!kinded_sort} but acts directly
19681968+ on the object [map]. *)
19691969+19701970+ val pp_kind : string fmt
19711971+ (** [pp_kind] formats kinds. *)
19721972+19731973+ val doc : 'a t -> string
19741974+ (** See {!Jsont.doc}. *)
19751975+19761976+ val with_doc : ?kind:string -> ?doc:string -> 'a t -> 'a t
19771977+ (** See {!Jsont.with_doc}. *)
19781978+19791979+ (** {1:errors Errors} *)
19801980+19811981+ val error_push_array :
19821982+ Meta.t -> ('array, 'elt, 'builder) array_map -> int node -> Error.t -> 'a
19831983+ (** [error_push_array] is like {!Error.push_array} but uses the
19841984+ given array [meta] and array map to caracterize the context. *)
19851985+19861986+ val error_push_object :
19871987+ Meta.t -> ('o, 'dec) object_map -> string node -> Error.t -> 'a
19881988+ (** [error_push_object] is like {!Error.push_object} but uses the
19891989+ given object [meta] and object map to caracterize the context. *)
19901990+19911991+ val type_error : Meta.t -> 'a t -> fnd:Sort.t -> 'b
19921992+ (** [type_error meta ~exp ~fnd] errors when kind [exp] was expected
19931993+ but sort [fnd] was found. *)
19941994+19951995+ val missing_mems_error :
19961996+ Meta.t -> ('o, 'o) object_map -> exp:mem_dec String_map.t ->
19971997+ fnd:string list -> 'a
19981998+ (** [missing_mems_error m map exp fnd] errors when [exp] cannot
19991999+ be found, [fnd] can list a few members that were found. *)
20002000+20012001+ val unexpected_mems_error :
20022002+ Meta.t -> ('o, 'o) object_map -> fnd:(string * Meta.t) list -> 'a
20032003+ (** [unexpected_mems_error meta map ~fnd] errors when [fnd] are
20042004+ unexpected members for object [map]. *)
20052005+20062006+ val unexpected_case_tag_error :
20072007+ Meta.t -> ('o, 'o) object_map -> ('o, 'd, 'tag) object_cases ->
20082008+ 'tag -> 'a
20092009+ (** [unexpected_case_tag_error meta map cases tag] is when a [tag]
20102010+ of a case member has no corresponding case. *)
20112011+20122012+ (** {1:toolbox Processor toolbox} *)
20132013+20142014+ val object_meta_arg : Meta.t Type.Id.t
20152015+ (** [object_meta_arg] holds the {!Jsont.Object.mem} to *)
20162016+20172017+ (** Heterogeneous dictionaries. *)
20182018+ module Dict : sig
20192019+ type binding = B : 'a Type.Id.t * 'a -> binding
20202020+ type t
20212021+ val empty : t
20222022+ val mem : 'a Type.Id.t -> t -> bool
20232023+ val add : 'a Type.Id.t -> 'a -> t -> t
20242024+ val remove : 'a Type.Id.t -> t -> t
20252025+ val find : 'a Type.Id.t -> t -> 'a option
20262026+ end
20272027+20282028+ val apply_dict : ('ret, 'f) dec_fun -> Dict.t -> 'f
20292029+ (** [apply_dict dec dict] applies [dict] to [f] in order to get the
20302030+ value ['f]. Raises [Invalid_argument] if [dict] has not all the
20312031+ type identifiers that [dec] needs. *)
20322032+20332033+ type unknown_mems_option =
20342034+ | Unknown_mems :
20352035+ ('o, 'mems, 'builder) unknown_mems option -> unknown_mems_option
20362036+ (** A type for hiding an optional {!type-unknown_mems} values. *)
20372037+20382038+ val override_unknown_mems :
20392039+ by:unknown_mems_option -> unknown_mems_option ->
20402040+ Dict.t -> unknown_mems_option * Dict.t
20412041+ (** [override_unknown_mems ~by current dict] preforms the unknown member
20422042+ overriding logic for {!Jsont.Object.Case} objects. In particular if
20432043+ [current] is a {!Jsont.Object.Mems.val-map} it adds an empty one in [dict]
20442044+ so that the associated decoding function does not fail. *)
20452045+20462046+ val finish_object_decode :
20472047+ ('o, 'o) object_map -> Meta.t -> ('p, 'mems, 'builder) unknown_mems ->
20482048+ 'builder -> mem_dec String_map.t -> Dict.t -> Dict.t
20492049+ (** [finish_object_decode map meta unknown_mems umap rem_mems dict] finishes
20502050+ an object map [map] decode. It adds the [umap] (if needed) to [dict],
20512051+ it adds [meta] to [dict] under {!object_meta_arg} and tries to find
20522052+ andd default values to [dict] for [rem_mems] (and errors if it can't). *)
20532053+20542054+ val pp_code : string fmt
20552055+ (** [pp_code] formats strings like code (in bold). *)
20562056+end
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* These three things should really belong to String. *)
77+88+let string_subrange ?(first = 0) ?last s =
99+ let max = String.length s - 1 in
1010+ let last = match last with
1111+ | None -> max
1212+ | Some l when l > max -> max
1313+ | Some l -> l
1414+ in
1515+ let first = if first < 0 then 0 else first in
1616+ if first > last then "" else
1717+ String.sub s first (last - first + 1)
1818+1919+let edit_distance s0 s1 =
2020+ let min_by f a b = if f a <= f b then a else b in
2121+ let max_by f a b = if f a <= f b then b else a in
2222+ let minimum a b c = min a (min b c) in
2323+ let s0 = min_by String.length s0 s1 (* row *)
2424+ and s1 = max_by String.length s0 s1 in (* column *)
2525+ let m = String.length s0 and n = String.length s1 in
2626+ let rec rows row0 row i =
2727+ if i > n then row0.(m) else begin
2828+ row.(0) <- i;
2929+ for j = 1 to m do
3030+ if s0.[j - 1] = s1.[i - 1] then row.(j) <- row0.(j - 1) else
3131+ row.(j) <- minimum (row0.(j - 1) + 1) (row0.(j) + 1) (row.(j - 1) + 1)
3232+ done;
3333+ rows row row0 (i + 1)
3434+ end in
3535+ rows (Array.init (m + 1) (fun x -> x)) (Array.make (m + 1) 0) 1
3636+3737+let suggest ?(dist = 2) candidates s =
3838+ let add (min, acc) name =
3939+ let d = edit_distance s name in
4040+ if d = min then min, (name :: acc) else
4141+ if d < min then d, [name] else
4242+ min, acc
4343+ in
4444+ let d, suggs = List.fold_left add (max_int, []) candidates in
4545+ if d <= dist (* suggest only if not too far *) then List.rev suggs else []
4646+4747+(* Hex converters *)
4848+4949+let lower_hex_digit n =
5050+ let n = n land 0xF in
5151+ Char.unsafe_chr (if n < 10 then 0x30 + n else 0x57 + n)
5252+5353+let binary_string_to_hex s =
5454+ let rec loop max s i h k =
5555+ if i > max then Bytes.unsafe_to_string h else
5656+ let byte = Char.code s.[i] in
5757+ Bytes.set h k (lower_hex_digit (byte lsr 4));
5858+ Bytes.set h (k + 1) (lower_hex_digit byte);
5959+ loop max s (i + 1) h (k + 2)
6060+ in
6161+ let len = String.length s in
6262+ let h = Bytes.create (2 * len) in
6363+ loop (len - 1) s 0 h 0
6464+6565+exception Illegal_hex of int
6666+6767+let binary_string_of_hex h =
6868+ let hex_value s i = match s.[i] with
6969+ | '0' .. '9' as c -> Char.code c - 0x30
7070+ | 'A' .. 'F' as c -> 10 + (Char.code c - 0x41)
7171+ | 'a' .. 'f' as c -> 10 + (Char.code c - 0x61)
7272+ | _ -> raise_notrace (Illegal_hex i)
7373+ in
7474+ try match String.length h with
7575+ | len when len mod 2 <> 0 -> raise (Illegal_hex len)
7676+ | len ->
7777+ let rec loop max s i h k =
7878+ if i > max then Ok (Bytes.unsafe_to_string s) else
7979+ let hi = hex_value h k and lo = hex_value h (k + 1) in
8080+ Bytes.set s i (Char.chr @@ (hi lsl 4) lor lo);
8181+ loop max s (i + 1) h (k + 2)
8282+ in
8383+ let s_len = len / 2 in
8484+ let s = Bytes.create s_len in
8585+ loop (s_len - 1) s 0 h 0
8686+ with Illegal_hex i ->
8787+ if i = String.length h
8888+ then Error "Missing final hexadecimal digit" else
8989+ let c = String.get_uint8 h i in
9090+ Error (Printf.sprintf "%d: byte x%x not an ASCII hexadecimal digit" i c)
9191+9292+(* Type identifiers. *)
9393+9494+module Type = struct (* Can be removed once we require OCaml 5.1 *)
9595+ type (_, _) eq = Equal : ('a, 'a) eq
9696+ module Id = struct
9797+ type _ id = ..
9898+ module type ID = sig type t type _ id += Id : t id end
9999+ type 'a t = (module ID with type t = 'a)
100100+101101+ let make (type a) () : a t =
102102+ (module struct type t = a type _ id += Id : t id end)
103103+104104+ let provably_equal
105105+ (type a b) ((module A) : a t) ((module B) : b t) : (a, b) eq option
106106+ =
107107+ match A.Id with B.Id -> Some Equal | _ -> None
108108+109109+ let uid (type a) ((module A) : a t) =
110110+ Obj.Extension_constructor.id (Obj.Extension_constructor.of_val A.Id)
111111+ end
112112+end
113113+114114+(* Resizable arrays *)
115115+116116+module Rarray = struct
117117+ type 'a t =
118118+ { mutable els : 'a array;
119119+ mutable max : int; (* index of last element of [els]. *) }
120120+121121+ let get a i = a.els.(i)
122122+ let empty () = { els = [||]; max = -1 }
123123+ let grow a v =
124124+ let len = a.max + 1 in
125125+ let els' = Array.make (2 * (if len = 0 then 1 else len)) v in
126126+ Array.blit a.els 0 els' 0 len; a.els <- els'
127127+128128+ let length a = a.max + 1
129129+ let add_last v a =
130130+ let max = a.max + 1 in
131131+ if max = Array.length a.els then grow a v;
132132+ a.max <- max; a.els.(max) <- v; a
133133+134134+ let to_array a =
135135+ if a.max + 1 = Array.length a.els then a.els else
136136+ let v = Array.make (a.max + 1) a.els.(0) in
137137+ Array.blit a.els 0 v 0 (a.max + 1);
138138+ v
139139+end
140140+141141+(* Resizable bigarrays *)
142142+143143+module Rbigarray1 = struct
144144+ type ('a, 'b, 'c) t =
145145+ { mutable els : ('a, 'b, 'c) Bigarray.Array1.t;
146146+ mutable max : int; (* index of the last element of [els]. *) }
147147+148148+ let get a i = Bigarray.Array1.get a.els i
149149+150150+ let empty kind layout =
151151+ { els = Bigarray.Array1.create kind layout 0; max = -1 }
152152+153153+ let grow a v =
154154+ let len = a.max + 1 in
155155+ let len = if len = 0 then 1 else len in
156156+ let init i = Bigarray.Array1.(if i <= a.max then get a.els i else v) in
157157+ let k, l = Bigarray.Array1.(kind a.els, layout a.els) in
158158+ let els' = Bigarray.Array1.init k l (2 * len) init in
159159+ a.els <- els'
160160+161161+ let length a = a.max + 1
162162+ let add_last v a =
163163+ let max = a.max + 1 in
164164+ if max = Bigarray.Array1.dim a.els then grow a v;
165165+ a.max <- max; Bigarray.Array1.set a.els max v; a
166166+167167+ let to_bigarray a =
168168+ if a.max + 1 = Bigarray.Array1.dim a.els then a.els else
169169+ let init i = Bigarray.Array1.get a.els i in
170170+ let k, l = Bigarray.Array1.(kind a.els, layout a.els) in
171171+ Bigarray.Array1.init k l (a.max + 1) init
172172+end
173173+174174+(* Mini fmt *)
175175+176176+module Fmt = struct
177177+ type 'a t = Format.formatter -> 'a -> unit
178178+ let pf = Format.fprintf
179179+ let str = Format.asprintf
180180+ let nop _ () = ()
181181+ let sp = Format.pp_print_space
182182+ let comma ppf () = Format.pp_print_char ppf ','; sp ppf ()
183183+ let list = Format.pp_print_list
184184+ let char = Format.pp_print_char
185185+ let string = Format.pp_print_string
186186+ let substring first len ppf s =
187187+ if first = 0 && len = String.length s then string ppf s else
188188+ (* One day use https://github.com/ocaml/ocaml/pull/12133 *)
189189+ for i = first to first + len - 1 do char ppf s.[i] done
190190+191191+ let lines ppf s =
192192+ Format.pp_print_list string ppf (String.split_on_char '\n' s)
193193+194194+ (* ANSI styling
195195+196196+ Note this is the scheme we have in More.Fmt but obviously
197197+ we can't depend on it. For now we decided not to surface it
198198+ at the library level. Ideally something should be provided
199199+ upstream. *)
200200+201201+ type styler = Ansi | Plain
202202+203203+ let styler' = Atomic.make @@
204204+ match Sys.getenv_opt "NO_COLOR" with
205205+ | Some s when s <> "" -> Plain
206206+ | _ ->
207207+ match Sys.getenv_opt "TERM" with
208208+ | Some "dumb" -> Plain
209209+ | None when Sys.backend_type <> Other "js_of_ocaml" -> Plain
210210+ | _ -> Ansi
211211+212212+ let set_styler styler = Atomic.set styler' styler
213213+ let styler () = Atomic.get styler'
214214+215215+ let ansi_reset = "\x1B[0m"
216216+ let bold ppf s =
217217+ if Atomic.get styler' = Plain then string ppf s else
218218+ pf ppf "@<0>%s%s@<0>%s" "\x1B[1m" s ansi_reset
219219+220220+ let bold_red ppf s =
221221+ if Atomic.get styler' = Plain then string ppf s else
222222+ pf ppf "@<0>%s%s@<0>%s" "\x1B[31;1m" s ansi_reset
223223+224224+ let code = bold
225225+ let puterr ppf () = bold_red ppf "Error"; char ppf ':'
226226+227227+ let disable_ansi_styler () = set_styler Plain
228228+229229+ (* HCI fragments *)
230230+231231+ let op_enum op ?(empty = nop) pp_v ppf = function
232232+ | [] -> empty ppf ()
233233+ | [v] -> pp_v ppf v
234234+ | _ as vs ->
235235+ let rec loop ppf = function
236236+ | [v0; v1] -> pf ppf "%a@ %s@ %a" pp_v v0 op pp_v v1
237237+ | v :: vs -> pf ppf "%a,@ " pp_v v; loop ppf vs
238238+ | [] -> assert false
239239+ in
240240+ loop ppf vs
241241+242242+ let or_enum ?empty pp_v ppf vs = op_enum "or" ?empty pp_v ppf vs
243243+244244+ let should_it_be pp_v ppf = function
245245+ | [] -> () | vs -> pf ppf "Should it be %a ?" (or_enum pp_v) vs
246246+247247+ let must_be pp_v ppf = function
248248+ | [] -> () | vs -> pf ppf "Must be %a." (or_enum pp_v) vs
249249+250250+ let unexpected ~kind pp_v ppf v = pf ppf "Unexpected %a: %a." kind () pp_v v
251251+ let unexpected' ~kind pp_v ~hint ppf (v, hints) = match hints with
252252+ | [] -> unexpected ~kind pp_v ppf v
253253+ | hints -> unexpected ~kind pp_v ppf v; sp ppf (); (hint pp_v) ppf hints
254254+255255+ let out_of_dom ?pp_kind () ppf (s, ss) =
256256+ let kind = match pp_kind with
257257+ | None -> fun ppf () -> string ppf "value" | Some pp_kind -> pp_kind
258258+ in
259259+ let hint, ss = match suggest ss s with
260260+ | [] -> must_be, ss | ss -> should_it_be, ss
261261+ in
262262+ pf ppf "@[%a@]" (unexpected' ~kind code ~hint) (s, ss)
263263+264264+ let similar_mems ppf (exp, fnd) = match suggest fnd exp with
265265+ | [] -> () | ms ->
266266+ pf ppf "@;@[Similar members in object: %a@]" (list ~pp_sep:comma code) ms
267267+268268+ let should_it_be_mem ppf (exp, fnd) = match suggest fnd exp with
269269+ | [] -> () | ms -> pf ppf "@;@[%a@]" (should_it_be code) ms
270270+271271+ (* JSON formatting *)
272272+273273+ type json_number_format = (float -> unit, Format.formatter, unit) format
274274+ let json_default_number_format : json_number_format = format_of_string "%.17g"
275275+276276+ let json_null ppf () = string ppf "null"
277277+ let json_bool ppf b = string ppf (if b then "true" else "false")
278278+ let json_number' fmt ppf f = (* cf. ECMAScript's JSON.stringify *)
279279+ if Float.is_finite f then pf ppf fmt f else json_null ppf ()
280280+281281+ let json_number ppf v = json_number' json_default_number_format ppf v
282282+ let json_string ppf s =
283283+ let is_control = function '\x00' .. '\x1F' | '\x7F' -> true | _ -> false in
284284+ let len = String.length s in
285285+ let max_idx = len - 1 in
286286+ let flush ppf start i =
287287+ if start < len then substring start (i - start) ppf s
288288+ in
289289+ let rec loop start i =
290290+ if i > max_idx then flush ppf start i else
291291+ let next = i + 1 in
292292+ match String.get s i with
293293+ | '"' -> flush ppf start i; string ppf "\\\""; loop next next
294294+ | '\\' -> flush ppf start i; string ppf "\\\\"; loop next next
295295+ | '\n' -> flush ppf start i; string ppf "\\n"; loop next next
296296+ | '\r' -> flush ppf start i; string ppf "\\r"; loop next next
297297+ | '\t' -> flush ppf start i; string ppf "\\t"; loop next next
298298+ | c when is_control c ->
299299+ flush ppf start i;
300300+ string ppf (Printf.sprintf "\\u%04X" (Char.code c));
301301+ loop next next
302302+ | _c -> loop start next
303303+ in
304304+ char ppf '"'; loop 0 0; char ppf '"'
305305+end
306306+307307+(* Text locations *)
308308+309309+module Textloc = struct
310310+311311+ (* File paths *)
312312+313313+ type fpath = string
314314+ let file_none = "-"
315315+ let pp_path = Format.pp_print_string
316316+317317+ (* Byte positions *)
318318+319319+ type byte_pos = int (* zero-based *)
320320+ let byte_pos_none = -1
321321+322322+ (* Lines *)
323323+324324+ type line_num = int (* one-based *)
325325+ let line_num_none = -1
326326+327327+ (* Line positions
328328+329329+ We keep the byte position of the first element on the line. This
330330+ first element may not exist and be equal to the text length if
331331+ the input ends with a newline. Editors expect tools to compute
332332+ visual columns (not a very good idea). By keeping these byte
333333+ positions we can approximate columns by subtracting the line byte
334334+ position data byte location. This will only be correct on
335335+ US-ASCII data. *)
336336+337337+ type line_pos = line_num * byte_pos
338338+ let line_pos_first = 1, 0
339339+ let line_pos_none = line_num_none, byte_pos_none
340340+341341+ (* Text locations *)
342342+343343+ type t =
344344+ { file : fpath;
345345+ first_byte : byte_pos; last_byte : byte_pos;
346346+ first_line : line_pos; last_line : line_pos }
347347+348348+ let make ~file ~first_byte ~last_byte ~first_line ~last_line =
349349+ { file; first_byte; last_byte; first_line; last_line }
350350+351351+ let file l = l.file
352352+ let set_file l file = { l with file }
353353+ let first_byte l = l.first_byte
354354+ let last_byte l = l.last_byte
355355+ let first_line l = l.first_line
356356+ let last_line l = l.last_line
357357+ let none =
358358+ let first_byte = byte_pos_none and last_byte = byte_pos_none in
359359+ let first_line = line_pos_none and last_line = line_pos_none in
360360+ make ~file:file_none ~first_byte ~last_byte ~first_line ~last_line
361361+362362+ (* Predicates and comparisons *)
363363+364364+ let is_none l = l.first_byte < 0
365365+ let is_empty l = l.first_byte > l.last_byte
366366+ let equal l0 l1 =
367367+ String.equal l0.file l1.file &&
368368+ Int.equal l0.first_byte l1.first_byte &&
369369+ Int.equal l0.last_byte l1.last_byte
370370+371371+ let compare l0 l1 =
372372+ let c = String.compare l0.file l1.file in
373373+ if c <> 0 then c else
374374+ let c = Int.compare l0.first_byte l1.first_byte in
375375+ if c <> 0 then c else
376376+ Int.compare l0.last_byte l1.last_byte
377377+378378+ (* Shrink and stretch *)
379379+380380+ let set_first l ~first_byte ~first_line = { l with first_byte; first_line }
381381+ let set_last l ~last_byte ~last_line = { l with last_byte; last_line }
382382+383383+ [@@@warning "-6"]
384384+ let to_first l =
385385+ make l.file l.first_byte l.first_byte l.first_line l.first_line
386386+387387+ let to_last l =
388388+ make l.file l.last_byte l.last_byte l.last_line l.last_line
389389+390390+ let before l =
391391+ make l.file l.first_byte byte_pos_none l.first_line line_pos_none
392392+393393+ let after l =
394394+ make l.file (l.first_byte + 1) byte_pos_none l.last_line line_pos_none
395395+ [@@@warning "+6"]
396396+397397+ let span l0 l1 =
398398+ let first_byte, first_line =
399399+ if l0.first_byte < l1.first_byte
400400+ then l0.first_byte, l0.first_line
401401+ else l1.first_byte, l1.first_line
402402+ in
403403+ let last_byte, last_line, file =
404404+ if l0.last_byte < l1.last_byte
405405+ then l1.last_byte, l1.last_line, l1.file
406406+ else l0.last_byte, l0.last_line, l0.file
407407+ in
408408+ make ~file ~first_byte ~first_line ~last_byte ~last_line
409409+410410+ [@@@warning "-6"]
411411+ let reloc ~first ~last =
412412+ make last.file first.first_byte last.last_byte first.first_line
413413+ last.last_line
414414+ [@@@warning "+6"]
415415+416416+ (* Formatters *)
417417+418418+ let pf = Format.fprintf
419419+ let pp_ocaml ppf l = match is_none l with
420420+ | true -> pf ppf "File \"%a\"" pp_path l.file
421421+ | false ->
422422+ let pp_lines ppf l = match fst l.first_line = fst l.last_line with
423423+ | true -> pf ppf "line %d" (fst l.first_line)
424424+ | false -> pf ppf "lines %d-%d" (fst l.first_line) (fst l.last_line)
425425+ in
426426+ (* "characters" represent positions (insertion points) not columns *)
427427+ let pos_s = l.first_byte - snd l.first_line in
428428+ let pos_e = l.last_byte - snd l.last_line + 1 in
429429+ if pos_s = 0 && pos_e = 0
430430+ then pf ppf "File \"%a\", %a" pp_path l.file pp_lines l
431431+ else pf ppf "File \"%a\", %a, characters %d-%d"
432432+ pp_path l.file pp_lines l pos_s pos_e
433433+434434+ let pp_gnu ppf l = match is_none l with
435435+ | true -> pf ppf "%a:" pp_path l.file
436436+ | false ->
437437+ let pp_lines ppf l =
438438+ let col_s = l.first_byte - snd l.first_line + 1 in
439439+ let col_e = l.last_byte - snd l.last_line + 1 in
440440+ match fst l.first_line = fst l.last_line with
441441+ | true -> pf ppf "%d.%d-%d" (fst l.first_line) col_s col_e
442442+ | false ->
443443+ pf ppf "%d.%d-%d.%d"
444444+ (fst l.first_line) col_s (fst l.last_line) col_e
445445+ in
446446+ pf ppf "%a:%a" pp_path l.file pp_lines l
447447+448448+ let pp = pp_ocaml
449449+450450+ let pp_dump ppf l =
451451+ pf ppf "file:%s bytes:%d-%d lines:(%d,%d)-(%d,%d)"
452452+ l.file l.first_byte l.last_byte (fst l.first_line)
453453+ (snd l.first_line) (fst l.last_line) (snd l.last_line)
454454+end
455455+456456+type 'a fmt = Stdlib.Format.formatter -> 'a -> unit
457457+458458+(* Node meta data *)
459459+460460+module Meta = struct
461461+ type t =
462462+ { textloc : Textloc.t;
463463+ ws_before : string;
464464+ ws_after : string; }
465465+466466+ let make ?(ws_before = "") ?(ws_after = "") textloc =
467467+ { textloc; ws_before; ws_after }
468468+469469+ let none = { textloc = Textloc.none; ws_before = ""; ws_after = "" }
470470+ let is_none m = none == m
471471+ let textloc m = m.textloc
472472+ let ws_before m = m.ws_before
473473+ let ws_after m = m.ws_after
474474+ let with_textloc m textloc = { m with textloc }
475475+ let clear_ws m = { m with ws_before = ""; ws_after = "" }
476476+ let clear_textloc m = { m with textloc = Textloc.none }
477477+ let copy_ws src ~dst =
478478+ { dst with ws_before = src.ws_before; ws_after = src.ws_after }
479479+end
480480+481481+type 'a node = 'a * Meta.t
482482+483483+(* JSON numbers *)
484484+485485+module Number = struct
486486+ let number_contains_int = Sys.int_size <= 53
487487+ let min_exact_int = if number_contains_int then Int.min_int else -(1 lsl 53)
488488+ let max_exact_int = if number_contains_int then Int.max_int else 1 lsl 53
489489+ let min_exact_uint8 = 0 let max_exact_uint8 = 255
490490+ let min_exact_uint16 = 0 let max_exact_uint16 = 65535
491491+ let min_exact_int8 = -128 let max_exact_int8 = 127
492492+ let min_exact_int16 = -32768 let max_exact_int16 = 32767
493493+ let min_exact_int32 = Int32.min_int let max_exact_int32 = Int32.max_int
494494+ let max_exact_int64 = Int64.shift_left 1L 53
495495+ let min_exact_int64 = Int64.neg max_exact_int64
496496+497497+ let[@inline] int_is_uint8 v = v land (lnot 0xFF) = 0
498498+ let[@inline] int_is_uint16 v = v land (lnot 0xFFFF) = 0
499499+ let[@inline] int_is_int8 v = min_exact_int8 <= v && v <= max_exact_int8
500500+ let[@inline] int_is_int16 v = min_exact_int16 <= v && v <= max_exact_int16
501501+502502+ let[@inline] can_store_exact_int v =
503503+ min_exact_int <= v && v <= max_exact_int
504504+505505+ let[@inline] can_store_exact_int64 v =
506506+ Int64.(compare min_exact_int64 v <= 0 && compare v max_exact_int64 <= 0)
507507+508508+ let max_exact_int_float = Int.to_float max_exact_int
509509+ let min_exact_int_float = Int.to_float min_exact_int
510510+ let max_exact_uint8_float = Int.to_float max_exact_uint8
511511+ let min_exact_uint8_float = Int.to_float min_exact_uint8
512512+ let max_exact_uint16_float = Int.to_float max_exact_uint16
513513+ let min_exact_uint16_float = Int.to_float min_exact_uint16
514514+ let max_exact_int8_float = Int.to_float max_exact_int8
515515+ let min_exact_int8_float = Int.to_float min_exact_int8
516516+ let min_exact_int16_float = Int.to_float min_exact_int16
517517+ let max_exact_int16_float = Int.to_float max_exact_int16
518518+ let max_exact_int32_float = Int32.to_float max_exact_int32
519519+ let min_exact_int32_float = Int32.to_float min_exact_int32
520520+ let max_exact_int64_float = Int64.to_float max_exact_int64
521521+ let min_exact_int64_float = Int64.to_float min_exact_int64
522522+523523+ let[@inline] in_exact_int_range v =
524524+ min_exact_int_float <= v && v <= max_exact_int_float
525525+526526+ let[@inline] in_exact_uint8_range v =
527527+ min_exact_uint8_float <= v && v <= max_exact_uint8_float
528528+529529+ let[@inline] in_exact_uint16_range v =
530530+ min_exact_uint16_float <= v && v <= max_exact_uint16_float
531531+532532+ let[@inline] in_exact_int8_range v =
533533+ min_exact_int8_float <= v && v <= max_exact_int8_float
534534+535535+ let[@inline] in_exact_int16_range v =
536536+ min_exact_int16_float <= v && v <= max_exact_int16_float
537537+538538+ let[@inline] in_exact_int32_range v =
539539+ min_exact_int32_float <= v && v <= max_exact_int32_float
540540+541541+ let[@inline] in_exact_int64_range v =
542542+ min_exact_int64_float <= v && v <= max_exact_int64_float
543543+end
544544+545545+(* JSON Paths *)
546546+547547+module Path = struct
548548+549549+ (* Indices *)
550550+551551+ type index = Mem of string node | Nth of int node
552552+553553+ let pp_name ppf n = Fmt.code ppf n
554554+ let pp_index_num ppf n = Fmt.code ppf (Int.to_string n)
555555+556556+ let pp_index ppf = function
557557+ | Mem (n, _) -> pp_name ppf n
558558+ | Nth (n, _) -> Fmt.pf ppf "[%a]" pp_index_num n
559559+560560+ let pp_index_trace ppf = function
561561+ | Mem (n, meta) ->
562562+ Fmt.pf ppf "%a: in member %a" Textloc.pp (Meta.textloc meta) pp_name n
563563+ | Nth (n, meta) ->
564564+ Fmt.pf ppf "%a: at index %a" Textloc.pp (Meta.textloc meta) pp_index_num n
565565+566566+ let pp_bracketed_index ppf = function
567567+ | Mem (n, _) -> Fmt.pf ppf "[%a]" pp_name n
568568+ | Nth (n, _) -> Fmt.pf ppf "[%a]" pp_index_num n
569569+570570+ (* Paths *)
571571+572572+ type t = index list
573573+ let root = []
574574+ let is_root = function [] -> true | _ -> false
575575+ let nth ?(meta = Meta.none) n p = Nth (n, meta) :: p
576576+ let mem ?(meta = Meta.none) n p = Mem (n, meta) :: p
577577+ let rev_indices p = p
578578+ let pp ppf is =
579579+ let pp_sep ppf () = Fmt.char ppf '.' in
580580+ Fmt.list ~pp_sep pp_index ppf (List.rev is)
581581+582582+ let pp_trace ppf is =
583583+ if is <> [] then Fmt.pf ppf "@,@[<v>%a@]" (Fmt.list pp_index_trace) is
584584+585585+ let none = []
586586+ let err i fmt = Format.kasprintf failwith ("%d: " ^^ fmt) i
587587+ let err_unexp_eoi i = err i "Unexpected end of input"
588588+ let err_unexp_char i s = err i "Unexpected character: %C" s.[i]
589589+ let err_illegal_char i s = err i "Illegal character here: %C" s.[i]
590590+ let err_unexp i s = err i "Unexpected input: %S" (string_subrange ~first:i s)
591591+592592+ (* Parsing *)
593593+594594+ let parse_eoi s i max = if i > max then () else err_unexp i s
595595+ let parse_index p s i max =
596596+ let first, stop = match s.[i] with '[' -> i + 1, ']' | _ -> i, '.' in
597597+ let last, next =
598598+ let rec loop stop s i max = match i > max with
599599+ | true -> if stop = ']' then err_unexp_eoi i else (i - 1), i
600600+ | false ->
601601+ let illegal = s.[i] = '[' || (s.[i] = ']' && stop = '.') in
602602+ if illegal then err_illegal_char i s else
603603+ if s.[i] <> stop then loop stop s (i + 1) max else
604604+ (i - 1), if stop = ']' then i + 1 else i
605605+ in
606606+ loop stop s first max
607607+ in
608608+ let idx = string_subrange ~first ~last s in
609609+ if idx = "" then err first "illegal empty index" else
610610+ match int_of_string idx with
611611+ | exception Failure _ -> next, (Mem (idx, Meta.none)) :: p
612612+ | idx -> next, (Nth (idx, Meta.none)) :: p
613613+614614+ let of_string s =
615615+ let rec loop p s i max =
616616+ if i > max then p else
617617+ let next, p = parse_index p s i max in
618618+ if next > max then p else
619619+ if s.[next] <> '.' then err_unexp_char next s else
620620+ if next + 1 <= max then loop p s (next + 1) max else
621621+ err_unexp_eoi next
622622+ in
623623+ try
624624+ if s = "" then Ok [] else
625625+ let start = if s.[0] = '.' then 1 else 0 in
626626+ Ok (loop [] s start (String.length s - 1))
627627+ with Failure e -> Error e
628628+end
629629+630630+(* JSON sorts *)
631631+632632+module Sort = struct
633633+ type t = Null | Bool | Number | String | Array | Object
634634+ let to_string = function
635635+ | Null -> "null" | Bool -> "bool" | Number -> "number"
636636+ | String -> "string" | Array -> "array" | Object -> "object"
637637+638638+ let kinded' ~kind:k s = if k = "" then s else String.concat " " [k; s]
639639+ let kinded ~kind sort = kinded' ~kind (to_string sort)
640640+ let or_kind ~kind sort = if kind <> "" then kind else (to_string sort)
641641+ let pp ppf s = Fmt.code ppf (to_string s)
642642+end
+188
vendor/opam/jsont/src/jsont_base.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** Low-level internal tools for {!Jsont}. *)
77+88+val string_subrange : ?first:int -> ?last:int -> string -> string
99+val binary_string_of_hex : string -> (string, string) result
1010+val binary_string_to_hex : string -> string
1111+1212+(** Type identifiers. Can be removed once we require OCaml 5.1 *)
1313+module Type : sig
1414+ type (_, _) eq = Equal : ('a, 'a) eq
1515+ module Id : sig
1616+ type 'a t
1717+ val make : unit -> 'a t
1818+ val uid : 'a t -> int
1919+ val provably_equal : 'a t -> 'b t -> ('a, 'b) eq option
2020+ end
2121+end
2222+2323+(** Resizable arrays. *)
2424+module Rarray : sig
2525+ type 'a t
2626+ val get : 'a t -> int -> 'a
2727+ val empty : unit -> 'a t
2828+ val grow : 'a t -> 'a -> unit
2929+ val length : 'a t -> int
3030+ val add_last : 'a -> 'a t -> 'a t
3131+ val to_array : 'a t -> 'a array
3232+end
3333+3434+(** Resizable bigarrays. *)
3535+module Rbigarray1 : sig
3636+ type ('a, 'b, 'c) t
3737+ val get : ('a, 'b, 'c) t -> int -> 'a
3838+ val empty : ('a, 'b) Bigarray.kind -> 'c Bigarray.layout -> ('a, 'b, 'c) t
3939+ val grow : ('a, 'b, 'c) t -> 'a -> unit
4040+ val length : ('a, 'b, 'c) t -> int
4141+ val add_last : 'a -> ('a, 'b, 'c) t -> ('a, 'b, 'c) t
4242+ val to_bigarray : ('a, 'b, 'c) t -> ('a, 'b, 'c) Bigarray.Array1.t
4343+end
4444+4545+(** Mini fmt *)
4646+module Fmt : sig
4747+ type 'a t = Format.formatter -> 'a -> unit
4848+ val pf : Format.formatter -> ('a, Format.formatter, unit) format -> 'a
4949+ val str : ('a, Format.formatter, unit, string) format4 -> 'a
5050+ val disable_ansi_styler : unit -> unit
5151+5252+ val nop : unit t
5353+ val sp : unit t
5454+ val list : ?pp_sep:unit t -> 'a t -> 'a list t
5555+ val char : char t
5656+ val string : string t
5757+ val substring : int -> int -> string t
5858+ val lines : string t
5959+ val bold : string t
6060+ val bold_red : string t
6161+ val code : string t
6262+ val puterr : unit t
6363+ val out_of_dom : ?pp_kind:unit t -> unit -> (string * string list) t
6464+ val should_it_be_mem : (string * string list) t
6565+ val similar_mems : (string * string list) t
6666+6767+6868+ type json_number_format = (float -> unit, Format.formatter, unit) format
6969+ val json_null : unit t
7070+ val json_bool : bool t
7171+ val json_default_number_format : json_number_format
7272+ val json_number' : json_number_format-> float t
7373+ val json_number : float t
7474+ val json_string : string t
7575+end
7676+7777+(** See {!Jsont.Textloc} *)
7878+module Textloc : sig
7979+ type fpath = string
8080+ val file_none : fpath
8181+8282+ type byte_pos = int
8383+ val byte_pos_none : byte_pos
8484+8585+ type line_num = int
8686+ val line_num_none : line_num
8787+8888+ type line_pos = line_num * byte_pos
8989+ val line_pos_first : line_pos
9090+ val line_pos_none : line_pos
9191+9292+ type t
9393+ val none : t
9494+ val make :
9595+ file:fpath -> first_byte:byte_pos -> last_byte:byte_pos ->
9696+ first_line:line_pos -> last_line:line_pos -> t
9797+9898+ val file : t -> fpath
9999+ val set_file : t -> fpath -> t
100100+ val first_byte : t -> byte_pos
101101+ val last_byte : t -> byte_pos
102102+ val first_line : t -> line_pos
103103+ val last_line : t -> line_pos
104104+ val is_none : t -> bool
105105+ val is_empty : t -> bool
106106+ val equal : t -> t -> bool
107107+ val compare : t -> t -> int
108108+ val set_first : t -> first_byte:byte_pos -> first_line:line_pos -> t
109109+ val set_last : t -> last_byte:byte_pos -> last_line:line_pos -> t
110110+ val to_first : t -> t
111111+ val to_last : t -> t
112112+ val before : t -> t
113113+ val after : t -> t
114114+ val span : t -> t -> t
115115+ val reloc : first:t -> last:t -> t
116116+ val pp_ocaml : Format.formatter -> t -> unit
117117+ val pp_gnu : Format.formatter -> t -> unit
118118+ val pp : Format.formatter -> t -> unit
119119+ val pp_dump : Format.formatter -> t -> unit
120120+end
121121+122122+type 'a fmt = Stdlib.Format.formatter -> 'a -> unit
123123+124124+(** See {!Jsont.Meta} *)
125125+module Meta : sig
126126+ type t
127127+ val make : ?ws_before:string -> ?ws_after:string -> Textloc.t -> t
128128+ val none : t
129129+ val is_none : t -> bool
130130+ val textloc : t -> Textloc.t
131131+ val ws_before : t -> string
132132+ val ws_after : t -> string
133133+ val with_textloc : t -> Textloc.t -> t
134134+ val clear_ws : t -> t
135135+ val clear_textloc : t -> t
136136+ val copy_ws : t -> dst:t -> t
137137+end
138138+139139+type 'a node = 'a * Meta.t
140140+141141+(** JSON number tools. *)
142142+module Number : sig
143143+ val number_contains_int : bool
144144+ val int_is_uint8 : int -> bool
145145+ val int_is_uint16 : int -> bool
146146+ val int_is_int8 : int -> bool
147147+ val int_is_int16 : int -> bool
148148+ val can_store_exact_int : int -> bool
149149+ val can_store_exact_int64 : Int64.t -> bool
150150+ val in_exact_int_range : float -> bool
151151+ val in_exact_uint8_range : float -> bool
152152+ val in_exact_uint16_range : float -> bool
153153+ val in_exact_int8_range : float -> bool
154154+ val in_exact_int16_range : float -> bool
155155+ val in_exact_int32_range : float -> bool
156156+ val in_exact_int64_range : float -> bool
157157+end
158158+159159+(** See {!Jsont.Path} *)
160160+module Path : sig
161161+ type index =
162162+ | Mem of string node
163163+ | Nth of int node
164164+165165+ val pp_index : index fmt
166166+ val pp_index_trace : index fmt
167167+168168+ type t
169169+ val root : t
170170+ val is_root : t -> bool
171171+ val nth : ?meta:Meta.t -> int -> t -> t
172172+ val mem : ?meta:Meta.t -> string -> t -> t
173173+ val rev_indices : t -> index list
174174+ val of_string : string -> (t, string) result
175175+ val pp : t fmt
176176+ val pp_trace : t fmt
177177+end
178178+179179+(** See {!Jsont.Sort} *)
180180+module Sort : sig
181181+ type t = Null | Bool | Number | String | Array | Object
182182+ val to_string : t -> string
183183+184184+ val kinded' : kind:string -> string -> string
185185+ val kinded : kind:string -> t -> string
186186+ val or_kind : kind:string -> t -> string
187187+ val pp : Format.formatter -> t -> unit
188188+end
+260
vendor/opam/jsont/test/cookbook.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: CC0-1.0
44+ ---------------------------------------------------------------------------*)
55+66+(* Dealing with null values. *)
77+88+let string_null_is_empty =
99+ let null = Jsont.null "" in
1010+ let enc = function "" -> null | _ -> Jsont.string in
1111+ Jsont.any ~dec_null:null ~dec_string:Jsont.string ~enc ()
1212+1313+1414+(* Base maps *)
1515+1616+module M = struct
1717+ type t = unit
1818+ let result_of_string s : (t, string) result = invalid_arg "unimplemented"
1919+ let of_string_or_failure s : t = invalid_arg "unimplemented"
2020+ let to_string v : string = invalid_arg "unimplemented"
2121+end
2222+2323+let m_jsont =
2424+ let dec = Jsont.Base.dec_result M.result_of_string in
2525+ let enc = Jsont.Base.enc M.to_string in
2626+ Jsont.Base.string (Jsont.Base.map ~kind:"M.t" ~dec ~enc ())
2727+2828+let m_jsont' =
2929+ let dec = Jsont.Base.dec_failure M.of_string_or_failure in
3030+ let enc = Jsont.Base.enc M.to_string in
3131+ Jsont.Base.string (Jsont.Base.map ~kind:"M.t" ~dec ~enc ())
3232+3333+let m_jsont'' =
3434+ Jsont.of_of_string ~kind:"M.t" M.result_of_string ~enc:M.to_string
3535+3636+(* Objects as records *)
3737+3838+module Person = struct
3939+ type t = { name : string; age : int }
4040+ let make name age = { name; age }
4141+ let name p = p.name
4242+ let age p = p.age
4343+ let jsont =
4444+ Jsont.Object.map ~kind:"Person" make
4545+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
4646+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
4747+ |> Jsont.Object.finish
4848+end
4949+5050+(* Objects as key-value maps *)
5151+5252+module String_map = Map.Make (String)
5353+5454+let map : ?kind:string -> 'a Jsont.t -> 'a String_map.t Jsont.t =
5555+fun ?kind t ->
5656+ Jsont.Object.map ?kind Fun.id
5757+ |> Jsont.Object.keep_unknown (Jsont.Object.Mems.string_map t) ~enc:Fun.id
5858+ |> Jsont.Object.finish
5959+6060+(* Optional members *)
6161+6262+module Person_opt_age = struct
6363+ type t = { name : string; age : int option }
6464+ let make name age = { name; age }
6565+ let name p = p.name
6666+ let age p = p.age
6767+ let jsont =
6868+ Jsont.Object.map ~kind:"Person" make
6969+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
7070+ |> Jsont.Object.mem "age" Jsont.(some int)
7171+ ~dec_absent:None ~enc_omit:Option.is_none ~enc:age
7272+ |> Jsont.Object.finish
7373+end
7474+7575+(* Unknown object members *)
7676+7777+module Person_strict = struct
7878+ type t = { name : string; age : int; }
7979+ let make name age = { name; age }
8080+ let name p = p.name
8181+ let age p = p.age
8282+ let jsont =
8383+ Jsont.Object.map ~kind:"Person" make
8484+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
8585+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
8686+ |> Jsont.Object.error_unknown
8787+ |> Jsont.Object.finish
8888+end
8989+9090+module Person_keep = struct
9191+ type t = { name : string; age : int; unknown : Jsont.json ; }
9292+ let make name age unknown = { name; age; unknown }
9393+ let name p = p.name
9494+ let age p = p.age
9595+ let unknown v = v.unknown
9696+ let jsont =
9797+ Jsont.Object.map ~kind:"Person" make
9898+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
9999+ |> Jsont.Object.mem "age" Jsont.int ~enc:age
100100+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
101101+ |> Jsont.Object.finish
102102+end
103103+104104+(* Dealing with recursive JSON *)
105105+106106+module Tree = struct
107107+ type 'a t = Node of 'a * 'a t list
108108+ let make v children = Node (v, children)
109109+ let value (Node (v, _)) = v
110110+ let children (Node (_, children)) = children
111111+ let jsont value_type =
112112+ let rec t = lazy
113113+ (Jsont.Object.map ~kind:"Tree" make
114114+ |> Jsont.Object.mem "value" value_type ~enc:value
115115+ |> Jsont.Object.mem "children" (Jsont.list (Jsont.rec' t)) ~enc:children
116116+ |> Jsont.Object.finish)
117117+ in
118118+ Lazy.force t
119119+end
120120+121121+(* Dealing with object types or classes *)
122122+123123+module Geometry_variant = struct
124124+ module Circle = struct
125125+ type t = { name : string; radius : float; }
126126+ let make name radius = { name; radius }
127127+ let name c = c.name
128128+ let radius c = c.radius
129129+ let jsont =
130130+ Jsont.Object.map ~kind:"Circle" make
131131+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
132132+ |> Jsont.Object.mem "radius" Jsont.number ~enc:radius
133133+ |> Jsont.Object.finish
134134+ end
135135+136136+ module Rect = struct
137137+ type t = { name : string; width : float; height : float }
138138+ let make name width height = { name; width; height }
139139+ let name r = r.name
140140+ let width r = r.width
141141+ let height r = r.height
142142+ let jsont =
143143+ Jsont.Object.map ~kind:"Rect" make
144144+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
145145+ |> Jsont.Object.mem "width" Jsont.number ~enc:width
146146+ |> Jsont.Object.mem "height" Jsont.number ~enc:height
147147+ |> Jsont.Object.finish
148148+ end
149149+150150+ type t = Circle of Circle.t | Rect of Rect.t
151151+ let circle c = Circle c
152152+ let rect r = Rect r
153153+ let jsont =
154154+ let circle = Jsont.Object.Case.map "Circle" Circle.jsont ~dec:circle in
155155+ let rect = Jsont.Object.Case.map "Rect" Rect.jsont ~dec:rect in
156156+ let enc_case = function
157157+ | Circle c -> Jsont.Object.Case.value circle c
158158+ | Rect r -> Jsont.Object.Case.value rect r
159159+ in
160160+ let cases = Jsont.Object.Case.[make circle; make rect] in
161161+ Jsont.Object.map ~kind:"Geometry" Fun.id
162162+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
163163+ |> Jsont.Object.finish
164164+end
165165+166166+module Geometry_record = struct
167167+ module Circle = struct
168168+ type t = { radius : float; }
169169+ let make radius = { radius }
170170+ let radius c = c.radius
171171+ let jsont =
172172+ Jsont.Object.map ~kind:"Circle" make
173173+ |> Jsont.Object.mem "radius" Jsont.number ~enc:radius
174174+ |> Jsont.Object.finish
175175+ end
176176+177177+ module Rect = struct
178178+ type t = { width : float; height : float }
179179+ let make width height = { width; height }
180180+ let width r = r.width
181181+ let height r = r.height
182182+ let jsont =
183183+ Jsont.Object.map ~kind:"Rect" make
184184+ |> Jsont.Object.mem "width" Jsont.number ~enc:width
185185+ |> Jsont.Object.mem "height" Jsont.number ~enc:height
186186+ |> Jsont.Object.finish
187187+ end
188188+189189+ type type' = Circle of Circle.t | Rect of Rect.t
190190+ let circle c = Circle c
191191+ let rect r = Rect r
192192+193193+ type t = { name : string; type' : type' }
194194+ let make name type' = { name; type' }
195195+ let name g = g.name
196196+ let type' g = g.type'
197197+198198+ let jsont =
199199+ let circle = Jsont.Object.Case.map "Circle" Circle.jsont ~dec:circle in
200200+ let rect = Jsont.Object.Case.map "Rect" Rect.jsont ~dec:rect in
201201+ let enc_case = function
202202+ | Circle c -> Jsont.Object.Case.value circle c
203203+ | Rect r -> Jsont.Object.Case.value rect r
204204+ in
205205+ let cases = Jsont.Object.Case.[make circle; make rect] in
206206+ Jsont.Object.map ~kind:"Geometry" make
207207+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
208208+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:type' ~enc_case cases
209209+ |> Jsont.Object.finish
210210+end
211211+212212+213213+(* Untagged object types *)
214214+215215+module Response = struct
216216+ type t =
217217+ { id : int;
218218+ value : (Jsont.json, string) result }
219219+220220+ let make id result error =
221221+ let pp_mem = Jsont.Repr.pp_code in
222222+ match result, error with
223223+ | Some result, None -> { id; value = Ok result }
224224+ | None, Some error -> { id; value = Error error }
225225+ | Some _ , Some _ ->
226226+ Jsont.Error.msgf Jsont.Meta.none "Both %a and %a members are defined"
227227+ pp_mem "result" pp_mem "error"
228228+ | None, None ->
229229+ Jsont.Error.msgf Jsont.Meta.none "Missing either %a or %a member"
230230+ pp_mem "result" pp_mem "error"
231231+232232+ let result r = match r.value with Ok v -> Some v | Error _ -> None
233233+ let error r = match r.value with Ok _ -> None | Error e -> Some e
234234+235235+ let jsont =
236236+ Jsont.Object.map make
237237+ |> Jsont.Object.mem "id" Jsont.int ~enc:(fun r -> r.id)
238238+ |> Jsont.Object.opt_mem "result" Jsont.json ~enc:result
239239+ |> Jsont.Object.opt_mem "error" Jsont.string ~enc:error
240240+ |> Jsont.Object.finish
241241+end
242242+243243+(* Flattening objects on queries *)
244244+245245+module Group = struct
246246+ type t = { id : int; name : string; persons : Person.t list }
247247+ let make id name persons = { id; name; persons }
248248+249249+ let info_jsont =
250250+ Jsont.Object.map make
251251+ |> Jsont.Object.mem "id" Jsont.int
252252+ |> Jsont.Object.mem "name" Jsont.string
253253+ |> Jsont.Object.finish
254254+255255+ let jsont =
256256+ Jsont.Object.map (fun k persons -> k persons)
257257+ |> Jsont.Object.mem "info" info_jsont
258258+ |> Jsont.Object.mem "persons" (Jsont.list Person.jsont)
259259+ |> Jsont.Object.finish
260260+end
···11+jsont: [31;1mError[0m: Expected [1mJSON value[0m but found [1m,[0m
22+ File "invalid-array0.json", line 1, characters 7-8:
33+ File "invalid-array0.json", line 1, characters 7-8: at index [1m2[0m of
44+ File "invalid-array0.json", line 1, characters 2-8: [1marray<json>[0m
···11+jsont: [31;1mError[0m: Expected [1m,[0m or [1m][0m after [1marray element[0m but found [1m4[0m
22+ File "invalid-array1.json", line 1, characters 11-12:
···11+jsont: [31;1mError[0m: Expected [1mJSON value[0m but found [1m][0m
22+ File "invalid-array2.json", line 1, characters 7-8:
33+ File "invalid-array2.json", line 1, characters 7-8: at index [1m3[0m of
44+ File "invalid-array2.json", line 1, characters 0-8: [1marray<json>[0m
···11+jsont: [31;1mError[0m: Expected [1mJSON value[0m but found [1mend of text[0m
22+ File "invalid-array3.json", line 2, characters 0-1:
33+ File "invalid-array3.json", line 2, characters 0-1: at index [1m3[0m of
44+ File "invalid-array3.json", lines 1-2, characters 0-1: [1marray<json>[0m
···11+jsont: [31;1mError[0m: Expected [1mJSON value[0m but found [1m}[0m
22+ File "invalid-obj0.json", line 1, characters 9-10:
33+ File "invalid-obj0.json": in member [1mbla[0m of
44+ File "invalid-obj0.json", line 1, characters 0-10: [1mobject[0m
···11+jsont: [31;1mError[0m: Expected [1m,[0m or [1m}[0m after [1mobject member[0m but found: [1mend of text[0m
22+ File "invalid-obj3.json", line 2, characters 0-1:
+324
vendor/opam/jsont/test/geojson.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* GeoJSON codec https://datatracker.ietf.org/doc/html/rfc7946
77+88+ Note: a few length constraints on arrays should be checked,
99+ a combinators should be added for that.
1010+1111+ In contrast to Topojson the structure is a bit more annoying to
1212+ model because there is subtyping on the "type" field: GeoJSON
1313+ objects can be Feature, FeatureCollection or any Geometry object
1414+ and Geometry objects are recursive on themselves (but not on
1515+ Feature or Feature collection) and FeatureCollection only have
1616+ Feature objects. We handle this by redoing the cases to handle only
1717+ the subsets. *)
1818+1919+type float_array = float array
2020+let float_array_jsont ~kind = Jsont.array ~kind Jsont.number
2121+2222+type 'a garray = 'a array
2323+let garray = Jsont.array
2424+2525+module Bbox = struct
2626+ type t = float_array
2727+ let jsont = float_array_jsont ~kind:"Bbox"
2828+end
2929+3030+module Position = struct
3131+ type t = float_array
3232+ let jsont = float_array_jsont ~kind:"Position"
3333+end
3434+3535+module Geojson_object = struct
3636+ type 'a t =
3737+ { type' : 'a;
3838+ bbox : Bbox.t option;
3939+ unknown : Jsont.json }
4040+4141+ let make type' bbox unknown = { type'; bbox; unknown }
4242+ let type' o = o.type'
4343+ let bbox o = o.bbox
4444+ let unknown o = o.unknown
4545+4646+ let finish_jsont map =
4747+ map
4848+ |> Jsont.Object.opt_mem "bbox" Bbox.jsont ~enc:bbox
4949+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
5050+ |> Jsont.Object.finish
5151+5252+ let geometry ~kind coordinates =
5353+ Jsont.Object.map ~kind make
5454+ |> Jsont.Object.mem "coordinates" coordinates ~enc:type'
5555+ |> finish_jsont
5656+end
5757+5858+module Point = struct
5959+ type t = Position.t
6060+ let jsont = Geojson_object.geometry ~kind:"Point" Position.jsont
6161+end
6262+6363+module Multi_point = struct
6464+ type t = Position.t garray
6565+ let jsont =
6666+ Geojson_object.geometry ~kind:"MultiPoint" (garray Position.jsont)
6767+end
6868+6969+module Line_string = struct
7070+ type t = Position.t garray
7171+ let jsont =
7272+ Geojson_object.geometry ~kind:"LineString" (garray Position.jsont)
7373+end
7474+7575+module Multi_line_string = struct
7676+ type t = Line_string.t garray
7777+ let jsont =
7878+ Geojson_object.geometry ~kind:"LineString" (garray (garray Position.jsont))
7979+end
8080+8181+module Polygon = struct
8282+ type t = Line_string.t garray
8383+ let jsont =
8484+ Geojson_object.geometry ~kind:"Polygon" (garray (garray Position.jsont))
8585+end
8686+8787+module Multi_polygon = struct
8888+ type t = Polygon.t garray
8989+ let jsont =
9090+ Geojson_object.geometry ~kind:"MultiPolygon"
9191+ (garray (garray (garray Position.jsont)))
9292+end
9393+9494+module Geojson = struct
9595+ type 'a object' = 'a Geojson_object.t
9696+ type geometry =
9797+ [ `Point of Point.t object'
9898+ | `Multi_point of Multi_point.t object'
9999+ | `Line_string of Line_string.t object'
100100+ | `Multi_line_string of Multi_line_string.t object'
101101+ | `Polygon of Polygon.t object'
102102+ | `Multi_polygon of Multi_polygon.t object'
103103+ | `Geometry_collection of geometry_collection object' ]
104104+ and geometry_collection = geometry list
105105+106106+ module Feature = struct
107107+ type id = [ `Number of float | `String of string ]
108108+ type t =
109109+ { id : id option;
110110+ geometry : geometry option;
111111+ properties : Jsont.json option; }
112112+113113+ let make id geometry properties = { id; geometry; properties }
114114+ let make_geojson_object id geometry properties =
115115+ Geojson_object.make (make id geometry properties)
116116+117117+ let id f = f.id
118118+ let geometry f = f.geometry
119119+ let properties f = f.properties
120120+121121+ type collection = t object' list
122122+ end
123123+124124+ type t =
125125+ [ `Feature of Feature.t object'
126126+ | `Feature_collection of Feature.collection object'
127127+ | geometry ]
128128+129129+ let point v = `Point v
130130+ let multi_point v = `Multi_point v
131131+ let line_string v = `Line_string v
132132+ let multi_line_string v = `Multi_line_string v
133133+ let polygon v = `Polygon v
134134+ let multi_polygon v = `Multi_polygon v
135135+ let geometry_collection vs = `Geometry_collection vs
136136+ let feature v = `Feature v
137137+ let feature_collection vs = `Feature_collection vs
138138+139139+ let feature_id_jsont =
140140+ let number =
141141+ let dec = Jsont.Base.dec (fun n -> `Number n) in
142142+ let enc = Jsont.Base.enc (function `Number n -> n | _ -> assert false) in
143143+ Jsont.Base.number (Jsont.Base.map ~enc ~dec ())
144144+ in
145145+ let string =
146146+ let dec = Jsont.Base.dec (fun n -> `String n) in
147147+ let enc = Jsont.Base.enc (function `String n -> n | _ -> assert false) in
148148+ Jsont.Base.string (Jsont.Base.map ~enc ~dec ())
149149+ in
150150+ let enc = function `Number _ -> number | `String _ -> string in
151151+ Jsont.any ~kind:"id" ~dec_number:number ~dec_string:string ~enc ()
152152+153153+ (* The first two Json types below handle subtyping by redoing
154154+ cases for subsets of types. *)
155155+156156+ let case_map obj dec = Jsont.Object.Case.map (Jsont.kind obj) obj ~dec
157157+158158+ let rec geometry_jsont = lazy begin
159159+ let case_point = case_map Point.jsont point in
160160+ let case_multi_point = case_map Multi_point.jsont multi_point in
161161+ let case_line_string = case_map Line_string.jsont line_string in
162162+ let case_multi_line_string =
163163+ case_map Multi_line_string.jsont multi_line_string
164164+ in
165165+ let case_polygon = case_map Polygon.jsont polygon in
166166+ let case_multi_polygon = case_map Multi_polygon.jsont multi_polygon in
167167+ let case_geometry_collection =
168168+ case_map (Lazy.force geometry_collection_jsont) geometry_collection
169169+ in
170170+ let enc_case = function
171171+ | `Point v -> Jsont.Object.Case.value case_point v
172172+ | `Multi_point v -> Jsont.Object.Case.value case_multi_point v
173173+ | `Line_string v -> Jsont.Object.Case.value case_line_string v
174174+ | `Multi_line_string v -> Jsont.Object.Case.value case_multi_line_string v
175175+ | `Polygon v -> Jsont.Object.Case.value case_polygon v
176176+ | `Multi_polygon v -> Jsont.Object.Case.value case_multi_polygon v
177177+ | `Geometry_collection v ->
178178+ Jsont.Object.Case.value case_geometry_collection v
179179+ in
180180+ let cases = Jsont.Object.Case.[
181181+ make case_point; make case_multi_point; make case_line_string;
182182+ make case_multi_line_string; make case_polygon; make case_multi_polygon;
183183+ make case_geometry_collection ]
184184+ in
185185+ Jsont.Object.map ~kind:"Geometry object" Fun.id
186186+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
187187+ ~tag_to_string:Fun.id ~tag_compare:String.compare
188188+ |> Jsont.Object.finish
189189+ end
190190+191191+ and feature_jsont : Feature.t object' Jsont.t Lazy.t = lazy begin
192192+ let case_feature = case_map (Lazy.force case_feature_jsont) Fun.id in
193193+ let enc_case v = Jsont.Object.Case.value case_feature v in
194194+ let cases = Jsont.Object.Case.[ make case_feature ] in
195195+ Jsont.Object.map ~kind:"Feature" Fun.id
196196+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
197197+ ~tag_to_string:Fun.id ~tag_compare:String.compare
198198+ |> Jsont.Object.finish
199199+ end
200200+201201+ and case_feature_jsont : Feature.t object' Jsont.t Lazy.t = lazy begin
202202+ Jsont.Object.map ~kind:"Feature" Feature.make_geojson_object
203203+ |> Jsont.Object.opt_mem "id" feature_id_jsont
204204+ ~enc:(fun o -> Feature.id (Geojson_object.type' o))
205205+ |> Jsont.Object.mem "geometry" (Jsont.option (Jsont.rec' geometry_jsont))
206206+ ~enc:(fun o -> Feature.geometry (Geojson_object.type' o))
207207+ |> Jsont.Object.mem "properties" (Jsont.option Jsont.json_object)
208208+ ~enc:(fun o -> Feature.properties (Geojson_object.type' o))
209209+ |> Geojson_object.finish_jsont
210210+ end
211211+212212+ and geometry_collection_jsont = lazy begin
213213+ Jsont.Object.map ~kind:"GeometryCollection" Geojson_object.make
214214+ |> Jsont.Object.mem "geometries" (Jsont.list (Jsont.rec' geometry_jsont))
215215+ ~enc:Geojson_object.type'
216216+ |> Geojson_object.finish_jsont
217217+ end
218218+219219+ and feature_collection_json = lazy begin
220220+ Jsont.Object.map ~kind:"FeatureCollection" Geojson_object.make
221221+ |> Jsont.Object.mem "features" Jsont.(list (Jsont.rec' feature_jsont))
222222+ ~enc:Geojson_object.type'
223223+ |> Geojson_object.finish_jsont
224224+ end
225225+226226+ and jsont : t Jsont.t Lazy.t = lazy begin
227227+ let case_point = case_map Point.jsont point in
228228+ let case_multi_point = case_map Multi_point.jsont multi_point in
229229+ let case_line_string = case_map Line_string.jsont line_string in
230230+ let case_multi_line_string =
231231+ case_map Multi_line_string.jsont multi_line_string
232232+ in
233233+ let case_polygon = case_map Polygon.jsont polygon in
234234+ let case_multi_polygon = case_map Multi_polygon.jsont multi_polygon in
235235+ let case_geometry_collection =
236236+ case_map (Lazy.force geometry_collection_jsont) geometry_collection
237237+ in
238238+ let case_feature = case_map (Lazy.force case_feature_jsont) feature in
239239+ let case_feature_collection =
240240+ case_map (Lazy.force feature_collection_json) feature_collection
241241+ in
242242+ let enc_case = function
243243+ | `Point v -> Jsont.Object.Case.value case_point v
244244+ | `Multi_point v -> Jsont.Object.Case.value case_multi_point v
245245+ | `Line_string v -> Jsont.Object.Case.value case_line_string v
246246+ | `Multi_line_string v -> Jsont.Object.Case.value case_multi_line_string v
247247+ | `Polygon v -> Jsont.Object.Case.value case_polygon v
248248+ | `Multi_polygon v -> Jsont.Object.Case.value case_multi_polygon v
249249+ | `Geometry_collection v ->
250250+ Jsont.Object.Case.value case_geometry_collection v
251251+ | `Feature v -> Jsont.Object.Case.value case_feature v
252252+ | `Feature_collection v -> Jsont.Object.Case.value case_feature_collection v
253253+ in
254254+ let cases = Jsont.Object.Case.[
255255+ make case_point; make case_multi_point; make case_line_string;
256256+ make case_multi_line_string; make case_polygon; make case_multi_polygon;
257257+ make case_geometry_collection; make case_feature;
258258+ make case_feature_collection ]
259259+ in
260260+ Jsont.Object.map ~kind:"GeoJSON" Fun.id
261261+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
262262+ ~tag_to_string:Fun.id ~tag_compare:String.compare
263263+ |> Jsont.Object.finish
264264+ end
265265+266266+ let jsont = Lazy.force jsont
267267+end
268268+269269+(* Command line interface *)
270270+271271+let ( let* ) = Result.bind
272272+let strf = Printf.sprintf
273273+274274+let log_if_error ~use = function
275275+| Ok v -> v
276276+| Error e ->
277277+ let lines = String.split_on_char '\n' e in
278278+ Format.eprintf "@[%a @[<v>%a@]@]"
279279+ Jsont.Error.puterr () (Format.pp_print_list Format.pp_print_string) lines;
280280+ use
281281+282282+let with_infile file f = (* XXX add something to bytesrw. *)
283283+ let process file ic = try Ok (f (Bytesrw.Bytes.Reader.of_in_channel ic)) with
284284+ | Sys_error e -> Error (Format.sprintf "@[<v>%s:@,%s@]" file e)
285285+ in
286286+ try match file with
287287+ | "-" -> process file In_channel.stdin
288288+ | file -> In_channel.with_open_bin file (process file)
289289+ with Sys_error e -> Error e
290290+291291+let trip ~file ~format ~locs ~dec_only =
292292+ log_if_error ~use:1 @@
293293+ with_infile file @@ fun r ->
294294+ log_if_error ~use:1 @@
295295+ let* t = Jsont_bytesrw.decode ~file ~locs Geojson.jsont r in
296296+ if dec_only then Ok 0 else
297297+ let w = Bytesrw.Bytes.Writer.of_out_channel stdout in
298298+ let* () = Jsont_bytesrw.encode ~format ~eod:true Geojson.jsont t w in
299299+ Ok 0
300300+301301+open Cmdliner
302302+open Cmdliner.Term.Syntax
303303+304304+let geojson =
305305+ Cmd.v (Cmd.info "geojson" ~doc:"round trip GeoJSON") @@
306306+ let+ file =
307307+ let doc = "$(docv) is the GeoJSON file. Use $(b,-) for stdin." in
308308+ Arg.(value & pos 0 string "-" & info [] ~doc ~docv:"FILE")
309309+ and+ locs =
310310+ let doc = "Preserve locations (better errors)." in
311311+ Arg.(value & flag & info ["l"; "locs"] ~doc)
312312+ and+ format =
313313+ let fmt = [ "indent", Jsont.Indent; "minify", Jsont.Minify ] in
314314+ let doc = strf "Output style. Must be %s." (Arg.doc_alts_enum fmt)in
315315+ Arg.(value & opt (enum fmt) Jsont.Minify &
316316+ info ["f"; "format"] ~doc ~docv:"FMT")
317317+ and+ dec_only =
318318+ let doc = "Decode only." in
319319+ Arg.(value & flag & info ["d"] ~doc)
320320+ in
321321+ trip ~file ~format ~locs ~dec_only
322322+323323+let main () = Cmd.eval' geojson
324324+let () = if !Sys.interactive then () else exit (main ())
+108
vendor/opam/jsont/test/json_rpc.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: CC0-1.0
44+ ---------------------------------------------------------------------------*)
55+66+(** JSON-RPC codec https://www.jsonrpc.org/ *)
77+88+(* JSON-RPC version *)
99+1010+type jsonrpc = [`V2]
1111+let jsonrpc_jsont = Jsont.enum ["2.0", `V2]
1212+1313+(* JSON-RPC identifiers *)
1414+1515+type id = [ `String of string | `Number of float | `Null ]
1616+let id_jsont : id Jsont.t =
1717+ let null = Jsont.null `Null in
1818+ let string =
1919+ let dec s = `String s in
2020+ let enc = function `String s -> s | _ -> assert false in
2121+ Jsont.map ~dec ~enc Jsont.string
2222+ in
2323+ let number =
2424+ let dec n = `Number n in
2525+ let enc = function `Number n -> n | _ -> assert false in
2626+ Jsont.map ~dec ~enc Jsont.number
2727+ in
2828+ let enc = function
2929+ | `Null -> null | `String _ -> string | `Number _ -> number
3030+ in
3131+ Jsont.any ~dec_null:null ~dec_string:string ~dec_number:number ~enc ()
3232+3333+(* JSON-RPC request object *)
3434+3535+type params = Jsont.json (* An array or object *)
3636+let params_jsont =
3737+ let enc = function
3838+ | Jsont.Object _ | Jsont.Array _ -> Jsont.json
3939+ | j ->
4040+ let meta = Jsont.Meta.none in
4141+ let fnd = Jsont.Sort.to_string (Jsont.Json.sort j) in
4242+ Jsont.Error.expected meta "object or array" ~fnd
4343+ in
4444+ let kind = "JSON-RPC params" in
4545+ Jsont.any ~kind ~dec_array:Jsont.json ~dec_object:Jsont.json ~enc ()
4646+4747+type request =
4848+ { jsonrpc : jsonrpc;
4949+ method' : string;
5050+ params : params option;
5151+ id : id option; }
5252+5353+let request jsonrpc method' params id = { jsonrpc; method'; params; id }
5454+let request_jsont : request Jsont.t =
5555+ Jsont.Object.map request
5656+ |> Jsont.Object.mem "jsonrpc" jsonrpc_jsont ~enc:(fun r -> r.jsonrpc)
5757+ |> Jsont.Object.mem "method" Jsont.string ~enc:(fun r -> r.method')
5858+ |> Jsont.Object.opt_mem "params" params_jsont ~enc:(fun r -> r.params)
5959+ |> Jsont.Object.opt_mem "id" id_jsont ~enc:(fun r -> r.id)
6060+ |> Jsont.Object.finish
6161+6262+(* JSON-RPC error objects *)
6363+6464+type error =
6565+ { code : int;
6666+ message : string;
6767+ data : Jsont.json option; }
6868+6969+let error code message data = { code; message; data }
7070+let error_jsont =
7171+ Jsont.Object.map error
7272+ |> Jsont.Object.mem "code" Jsont.int ~enc:(fun e -> e.code)
7373+ |> Jsont.Object.mem "message" Jsont.string ~enc:(fun e -> e.message)
7474+ |> Jsont.Object.opt_mem "data" Jsont.json ~enc:(fun e -> e.data)
7575+ |> Jsont.Object.finish
7676+7777+(* JSON-RPC response object *)
7878+7979+type response =
8080+ { jsonrpc : jsonrpc;
8181+ value : (Jsont.json, error) result;
8282+ id : id; }
8383+8484+let response jsonrpc result error id : response =
8585+ let err_both () =
8686+ Jsont.Error.msgf Jsont.Meta.none "Both %a and %a members are defined"
8787+ Jsont.Repr.pp_code "result" Jsont.Repr.pp_code "error"
8888+ in
8989+ let err_none () =
9090+ Jsont.Error.msgf Jsont.Meta.none "Missing either %a or %a member"
9191+ Jsont.Repr.pp_code "result" Jsont.Repr.pp_code "error"
9292+ in
9393+ match result, error with
9494+ | Some result, None -> { jsonrpc; value = Ok result; id }
9595+ | None, Some error -> { jsonrpc; value = Error error; id }
9696+ | Some _ , Some _ -> err_both ()
9797+ | None, None -> err_none ()
9898+9999+let response_result r = match r.value with Ok v -> Some v | Error _ -> None
100100+let response_error r = match r.value with Ok _ -> None | Error e -> Some e
101101+102102+let response_jsont : response Jsont.t =
103103+ Jsont.Object.map response
104104+ |> Jsont.Object.mem "jsonrpc" jsonrpc_jsont ~enc:(fun r -> r.jsonrpc)
105105+ |> Jsont.Object.opt_mem "result" Jsont.json ~enc:response_result
106106+ |> Jsont.Object.opt_mem "error" error_jsont ~enc:response_error
107107+ |> Jsont.Object.mem "id" id_jsont ~enc:(fun r -> r.id)
108108+ |> Jsont.Object.finish
+429
vendor/opam/jsont/test/jsont_tool.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+let ( let* ) = Result.bind
77+88+let strf = Format.asprintf
99+let log_if_error ~use = function
1010+| Ok v -> v
1111+| Error e ->
1212+ let exec = Filename.basename Sys.executable_name in
1313+ let lines = String.split_on_char '\n' e in
1414+ Format.eprintf "%s: %a @[<v>%a@]@."
1515+ exec Jsont.Error.puterr () Format.(pp_print_list pp_print_string) lines;
1616+ use
1717+1818+let exit_err_file = 1
1919+let exit_err_json = 2
2020+let exit_err_diff = 3
2121+2222+module Os = struct
2323+2424+ (* Emulate B0_std.Os functionality to eschew the dep.
2525+ Note: this is only used for the [diff] function. *)
2626+2727+ let read_file file =
2828+ try
2929+ let ic = if file = "-" then stdin else open_in_bin file in
3030+ let finally () = if file = "-" then () else close_in_noerr ic in
3131+ Fun.protect ~finally @@ fun () -> Ok (In_channel.input_all ic)
3232+ with
3333+ | Sys_error err -> Error err
3434+3535+ let write_file file s =
3636+ try
3737+ let oc = if file = "-" then stdout else open_out_bin file in
3838+ let finally () = if file = "-" then () else close_out_noerr oc in
3939+ Fun.protect ~finally @@ fun () -> Ok (Out_channel.output_string oc s)
4040+ with
4141+ | Sys_error err -> Error err
4242+4343+ let with_tmp_dir f =
4444+ try
4545+ let tmpdir =
4646+ let file = Filename.temp_file "cmarkit" "dir" in
4747+ (Sys.remove file; Sys.mkdir file 0o700; file)
4848+ in
4949+ let finally () = try Sys.rmdir tmpdir with Sys_error _ -> () in
5050+ Fun.protect ~finally @@ fun () -> Ok (f tmpdir)
5151+ with
5252+ | Sys_error err -> Error ("Making temporary dir: " ^ err)
5353+5454+ let with_cwd cwd f =
5555+ try
5656+ let curr = Sys.getcwd () in
5757+ let () = Sys.chdir cwd in
5858+ let finally () = try Sys.chdir curr with Sys_error _ -> () in
5959+ Fun.protect ~finally @@ fun () -> Ok (f ())
6060+ with
6161+ | Sys_error err -> Error ("With cwd: " ^ err)
6262+end
6363+6464+let diff src fmted =
6565+ let env = ["GIT_CONFIG_SYSTEM=/dev/null"; "GIT_CONFIG_GLOBAL=/dev/null"; ] in
6666+ let set_env = match Sys.win32 with
6767+ | true -> String.concat "" (List.map (fun e -> "set " ^ e ^ " && ") env)
6868+ | false -> String.concat " " env
6969+ in
7070+ let diff = "git diff --ws-error-highlight=all --no-index --patience " in
7171+ let src_file = "src" and fmted_file = "fmt" in
7272+ let cmd = String.concat " " [set_env; diff; src_file; fmted_file] in
7373+ Result.join @@ Result.join @@ Os.with_tmp_dir @@ fun dir ->
7474+ Os.with_cwd dir @@ fun () ->
7575+ let* () = Os.write_file src_file src in
7676+ let* () = Os.write_file fmted_file fmted in
7777+ Ok (Sys.command cmd)
7878+7979+let with_infile file f = (* XXX add something to bytesrw. *)
8080+ let process file ic = try Ok (f (Bytesrw.Bytes.Reader.of_in_channel ic)) with
8181+ | Sys_error e -> Error (Format.sprintf "@[<v>%s:@,%s@]" file e)
8282+ in
8383+ try match file with
8484+ | "-" -> process file In_channel.stdin
8585+ | file -> In_channel.with_open_bin file (process file)
8686+ with Sys_error e -> Error e
8787+8888+let output ~format ~number_format j = match format with
8989+| `Pretty -> Ok (Format.printf "@[%a@]@." (Jsont.pp_json' ~number_format ()) j)
9090+| `Format format ->
9191+ let w = Bytesrw.Bytes.Writer.of_out_channel stdout in
9292+ Jsont_bytesrw.encode ~format ~number_format ~eod:true Jsont.json j w
9393+9494+let output_string ~format ~number_format j = match format with
9595+| `Pretty -> Ok (Format.asprintf "@[%a@]" (Jsont.pp_json' ~number_format ()) j)
9696+| `Format format ->
9797+ Jsont_bytesrw.encode_string ~format ~number_format Jsont.json j
9898+9999+let trip_type
100100+ ?(dec_only = false) ~file ~format ~number_format ~diff:do_diff ~locs t
101101+ =
102102+ log_if_error ~use:exit_err_file @@
103103+ with_infile file @@ fun r ->
104104+ log_if_error ~use:exit_err_json @@
105105+ let layout = format = `Format Jsont.Layout in
106106+ match do_diff with
107107+ | false ->
108108+ let* j = Jsont_bytesrw.decode ~file ~layout ~locs t r in
109109+ if dec_only then Ok 0 else
110110+ let* () = output ~format ~number_format j in
111111+ Ok 0
112112+ | true ->
113113+ let src = Bytesrw.Bytes.Reader.to_string r in
114114+ let* j = Jsont_bytesrw.decode_string ~file ~layout ~locs t src in
115115+ let* fmted = output_string ~format ~number_format j in
116116+ (match diff src fmted with
117117+ | Ok exit -> if exit = 0 then Ok 0 else Ok exit_err_diff
118118+ | Error e -> Format.eprintf "%s" e; Ok Cmdliner.Cmd.Exit.some_error)
119119+120120+let delete ~file ~path ~format ~number_format ~diff ~allow_absent ~locs =
121121+ let del = Jsont.delete_path ~allow_absent path in
122122+ trip_type ~file ~format ~number_format ~diff ~locs del
123123+124124+let fmt ~file ~format ~number_format ~diff ~locs ~dec_only =
125125+ trip_type ~file ~format ~number_format ~diff ~locs ~dec_only Jsont.json
126126+127127+let get ~file ~path ~format ~number_format ~diff ~absent ~locs =
128128+ let get = Jsont.path ?absent path Jsont.json in
129129+ trip_type ~file ~format ~number_format ~diff ~locs get
130130+131131+let locs' ~file =
132132+ let pf = Format.fprintf in
133133+ let pp_code = Jsont.Repr.pp_code in
134134+ let pp_locs_outline ppf v =
135135+ let indent = 2 in
136136+ let loc label ppf m =
137137+ pf ppf "@[<v>%s:@,%a@]@,"
138138+ label Jsont.Textloc.pp_ocaml (Jsont.Meta.textloc m)
139139+ in
140140+ let rec value ppf = function
141141+ | Jsont.Null ((), m) ->
142142+ loc (strf "%a" pp_code (strf "%a" Jsont.pp_null ())) ppf m
143143+ | Jsont.Bool (b, m) ->
144144+ loc (strf "Bool %a" pp_code (strf "%a" Jsont.pp_bool b)) ppf m
145145+ | Jsont.Number (n, m) ->
146146+ loc (strf "Number %a" pp_code (strf "%a" Jsont.pp_number n)) ppf m
147147+ | Jsont.String (s, m) ->
148148+ loc (strf "String %a" pp_code (strf "%a" Jsont.pp_string s)) ppf m
149149+ | Jsont.Array (l, m) ->
150150+ Format.pp_open_vbox ppf indent;
151151+ loc "Array" ppf m; (Format.pp_print_list value) ppf l;
152152+ Format.pp_close_box ppf ()
153153+ | Jsont.Object (o, m) ->
154154+ let mem ppf ((name, m), v) =
155155+ let l = strf "Member %a" pp_code (strf "%a" Jsont.pp_string name) in
156156+ loc l ppf m; value ppf v;
157157+ in
158158+ Format.pp_open_vbox ppf indent;
159159+ loc "Object" ppf m; (Format.pp_print_list mem) ppf o;
160160+ Format.pp_close_box ppf ()
161161+ in
162162+ value ppf v
163163+ in
164164+ log_if_error ~use:exit_err_file @@
165165+ with_infile file @@ fun reader ->
166166+ log_if_error ~use:exit_err_json @@
167167+ let* j = Jsont_bytesrw.decode ~file ~locs:true Jsont.json reader in
168168+ pp_locs_outline Format.std_formatter j;
169169+ Ok 0
170170+171171+let set
172172+ ~file ~path ~format ~number_format ~diff ~allow_absent ~stub ~json:j ~locs
173173+ =
174174+ let set = Jsont.set_path ?stub ~allow_absent Jsont.json path j in
175175+ trip_type ~file ~format ~number_format ~diff ~locs set
176176+177177+(* Command line interface *)
178178+179179+open Cmdliner
180180+open Cmdliner.Term.Syntax
181181+182182+let exits =
183183+ Cmd.Exit.info exit_err_file ~doc:"on file read errors." ::
184184+ Cmd.Exit.info exit_err_json ~doc:"on JSON parse or path errors." ::
185185+ Cmd.Exit.info exit_err_diff ~doc:"on JSON output differences." ::
186186+ Cmd.Exit.defaults
187187+188188+let path_arg = Arg.conv' ~docv:"JSON_PATH" Jsont.Path.(of_string, pp)
189189+let json_arg =
190190+ let of_string s =
191191+ Jsont_bytesrw.decode_string ~locs:true ~layout:true Jsont.json s
192192+ in
193193+ let pp = Jsont.pp_json in
194194+ Arg.conv' ~docv:"JSON" (of_string, pp)
195195+196196+let format_opt ~default =
197197+ let fmt =
198198+ [ "indent", `Format Jsont.Indent;
199199+ "minify", `Format Jsont.Minify;
200200+ "preserve", `Format Jsont.Layout;
201201+ "pretty", `Pretty ]
202202+ in
203203+ let doc =
204204+ strf "Output style. Must be %s. $(b,minify) guarantess there is \
205205+ no CR (U+000D) or LF (U+000A) in the output. $(b,pretty) is \
206206+ similar to $(b,indent) but may yield more compact outputs."
207207+ (Arg.doc_alts_enum fmt)
208208+ in
209209+ Arg.(value & opt (enum fmt) default & info ["f"; "format"] ~doc ~docv:"FMT")
210210+211211+let format_opt_default_pretty = format_opt ~default:`Pretty
212212+let format_opt_default_preserve = format_opt ~default:(`Format Jsont.Layout)
213213+214214+let allow_absent_opt =
215215+ let doc = "Do not error if $(i,JSON_PATH) does not exist." in
216216+ Arg.(value & flag & info ["a"; "allow-absent"] ~doc)
217217+218218+let locs_default_false =
219219+ let doc = "Keep track of source locations (improves error messages)." in
220220+ Arg.(value & flag & info ["locs"] ~doc)
221221+222222+let locs_default_true =
223223+ let doc = "Do not keep track of source locations." in
224224+ Term.(const ( not ) $ Arg.(value & flag & info ["no-locs"] ~doc))
225225+226226+let number_format_opt =
227227+ let doc = "Use C float format string $(docv) to format JSON numbers." in
228228+ let number_format : Jsont.number_format Arg.conv =
229229+ let parse s =
230230+ try Ok (Scanf.format_from_string s Jsont.default_number_format) with
231231+ | Scanf.Scan_failure _ ->
232232+ Error (strf "Cannot format a float with %S" s)
233233+ in
234234+ let pp ppf fmt = Format.pp_print_string ppf (string_of_format fmt) in
235235+ Arg.conv' (parse, pp)
236236+ in
237237+ Arg.(value & opt number_format Jsont.default_number_format &
238238+ info ["n"; "number-format"] ~doc ~docv:"FMT")
239239+240240+let diff_flag =
241241+ let doc =
242242+ "Output diff between input and output (needs $(b,git) in \
243243+ your $(b,PATH)). Exits with 0 only there are no differences."
244244+ in
245245+ Arg.(value & flag & info ["diff"] ~doc)
246246+247247+let dec_only =
248248+ let doc = "Decode only, no output." in
249249+ Arg.(value & flag & info ["d"; "decode-only"] ~doc)
250250+251251+let file_pos ~pos:p =
252252+ let doc = "$(docv) is the JSON file. Use $(b,-) for stdin." in
253253+ Arg.(value & pos p string "-" & info [] ~doc ~docv:"FILE")
254254+255255+let file_pos0 = file_pos ~pos:0
256256+let file_pos1 = file_pos ~pos:1
257257+let file_pos2 = file_pos ~pos:2
258258+259259+let common_man =
260260+ [ `S Manpage.s_bugs;
261261+ `P "This program is distributed with the jsont OCaml library. \
262262+ See $(i,https://erratique.ch/software/jsont) for contact \
263263+ information."; ]
264264+265265+let delete_cmd =
266266+ let doc = "Delete the value indexed by a JSON path" in
267267+ let sdocs = Manpage.s_common_options in
268268+ let man = [
269269+ `S Manpage.s_description;
270270+ `P "$(iname) deletes the value indexed by a JSON path. Outputs $(b,null) \
271271+ on the root path $(b,'.'). Examples:";
272272+ `Pre "$(iname) $(b,keywords.[0] package.json)"; `Noblank;
273273+ `Pre "$(iname) $(b,-a keywords.[0] package.json)";
274274+ `Blocks common_man; ]
275275+ in
276276+ let path_opt =
277277+ let doc = "Delete JSON path $(docv)." and docv = "JSON_PATH" in
278278+ Arg.(required & pos 0 (some path_arg) None & info [] ~doc ~docv)
279279+ in
280280+ Cmd.v (Cmd.info "delete" ~doc ~sdocs ~exits ~man) @@
281281+ let+ file = file_pos1
282282+ and+ path = path_opt
283283+ and+ format = format_opt_default_preserve
284284+ and+ number_format = number_format_opt
285285+ and+ diff = diff_flag
286286+ and+ allow_absent = allow_absent_opt
287287+ and+ locs = locs_default_true in
288288+ delete ~file ~path ~format ~number_format ~diff ~allow_absent ~locs
289289+290290+let fmt_cmd =
291291+ let doc = "Format JSON" in
292292+ let sdocs = Manpage.s_common_options in
293293+ let man = [
294294+ `S Manpage.s_description;
295295+ `P "$(iname) formats JSON. Examples:";
296296+ `Pre "$(iname) $(b,package.json)"; `Noblank;
297297+ `Pre "$(iname) $(b,-f minify package.json)";
298298+ `Blocks common_man; ]
299299+ in
300300+ Cmd.v (Cmd.info "fmt" ~doc ~sdocs ~exits ~man) @@
301301+ let+ file = file_pos0
302302+ and+ format = format_opt_default_pretty
303303+ and+ number_format = number_format_opt
304304+ and+ diff = diff_flag
305305+ and+ locs = locs_default_false
306306+ and+ dec_only = dec_only in
307307+ fmt ~file ~format ~number_format ~diff ~locs ~dec_only
308308+309309+let get_cmd =
310310+ let doc = "Extract the value indexed by a JSON path" in
311311+ let sdocs = Manpage.s_common_options in
312312+ let man = [
313313+ `S Manpage.s_description;
314314+ `P "$(iname) outputs the value indexed by a JSON path. Examples:";
315315+ `Pre "$(iname) $(b,'keywords.[0]' package.json)"; `Noblank;
316316+ `Pre "$(iname) $(b,-a 'null' 'keywords.[0]' package.json)"; `Noblank;
317317+ `Pre "$(iname) $(b,-a '[]' 'keywords' package.json)"; `Noblank;
318318+ `Pre "$(iname) $(b,'.' package.json)";
319319+ `Blocks common_man; ]
320320+ in
321321+ let path_pos =
322322+ let doc = "Extract the value indexed by JSON path $(docv)." in
323323+ Arg.(required & pos 0 (some path_arg) None & info [] ~doc ~docv:"JSON_PATH")
324324+ in
325325+ let absent_opt =
326326+ let doc = "Do not error if $(i,JSON_PATH) does not exist, output $(docv) \
327327+ instead."
328328+ in
329329+ Arg.(value & opt (some json_arg) None &
330330+ info ["a"; "absent"] ~doc ~docv:"JSON")
331331+ in
332332+ Cmd.v (Cmd.info "get" ~doc ~sdocs ~exits ~man) @@
333333+ let+ file = file_pos1
334334+ and+ path = path_pos
335335+ and+ format = format_opt_default_pretty
336336+ and+ number_format = number_format_opt
337337+ and+ diff = diff_flag
338338+ and+ absent = absent_opt
339339+ and+ locs = locs_default_true in
340340+ get ~file ~path ~format ~number_format ~diff ~absent ~locs
341341+342342+let set_cmd =
343343+ let doc = "Set the value indexed by a JSON path" in
344344+ let sdocs = Manpage.s_common_options in
345345+ let man = [
346346+ `S Manpage.s_description;
347347+ `P "$(iname) sets the value indexed by a JSON path. Examples:";
348348+ `Pre "$(iname) $(b,keywords '[\"codec\"]' package.json)"; `Noblank;
349349+ `Pre "$(iname) $(b,keywords.[0] '\"codec\"' package.json)"; `Noblank;
350350+ `Pre "$(iname) $(b,-a keywords.[4] '\"codec\"' package.json)"; `Noblank;
351351+ `Pre "$(iname) $(b,-s null -a keywords.[4] '\"codec\"' package.json)";
352352+ `Blocks common_man; ]
353353+ in
354354+ let path_pos =
355355+ let doc = "Set the value indexed by JSON path $(docv)." in
356356+ Arg.(required & pos 0 (some path_arg) None & info [] ~doc ~docv:"JSON_PATH")
357357+ in
358358+ let json_pos =
359359+ let doc = "Set value to $(docv)." in
360360+ Arg.(required & pos 1 (some json_arg) None & info [] ~doc ~docv:"JSON")
361361+ in
362362+ let stub =
363363+ let doc =
364364+ "Use $(b,docv) as a stub value to use if an array needs to be extended \
365365+ when $(b,-a) is used. By default uses the natural zero of the \
366366+ set data: null for null, false for booleans, 0 for numbers, empty
367367+ string for strings, empty array for array, empty object for object."
368368+ in
369369+ Arg.(value & opt (some json_arg) None & info ["s"; "stub"] ~doc
370370+ ~docv:"JSON")
371371+ in
372372+ Cmd.v (Cmd.info "set" ~doc ~sdocs ~exits ~man) @@
373373+ let+ file = file_pos2
374374+ and+ path = path_pos
375375+ and+ json = json_pos
376376+ and+ stub = stub
377377+ and+ format = format_opt_default_preserve
378378+ and+ number_format = number_format_opt
379379+ and+ diff = diff_flag
380380+ and+ allow_absent = allow_absent_opt
381381+ and+ locs = locs_default_true in
382382+ set ~file ~path ~format ~number_format ~diff ~allow_absent ~stub ~json ~locs
383383+384384+let locs_cmd =
385385+ let doc = "Show JSON parse locations" in
386386+ let sdocs = Manpage.s_common_options in
387387+ let man = [
388388+ `S Manpage.s_description;
389389+ `P "$(tname) outputs JSON parse locations. Example:";
390390+ `Pre "$(iname) $(b,package.json)";
391391+ `Blocks common_man; ]
392392+ in
393393+ Cmd.v (Cmd.info "locs" ~doc ~sdocs ~exits ~man) @@
394394+ let+ file = file_pos0 in
395395+ locs' ~file
396396+397397+let jsont =
398398+ let doc = "Process JSON data" in
399399+ let sdocs = Manpage.s_common_options in
400400+ let man = [
401401+ `S Manpage.s_description;
402402+ `P "$(mname) processes JSON data in various ways.";
403403+ `Pre "$(b,curl -L URL) | $(mname) $(b,fmt)"; `Noblank;
404404+ `Pre "$(mname) $(b,fmt package.json)"; `Noblank;
405405+ `Pre "$(mname) $(b,get 'keywords.[0]' package.json)"; `Noblank;
406406+ `Pre "$(mname) $(b,set 'keywords.[0]' '\"codec\"' package.json)"; `Noblank;
407407+ `Pre "$(mname) $(b,delete 'keywords.[0]' package.json)";
408408+ `P "More information about $(b,jsont)'s JSON paths is in the section \
409409+ JSON PATHS below.";
410410+ `S Manpage.s_commands;
411411+ `S Manpage.s_common_options;
412412+ `S "JSON PATHS";
413413+ `P "For $(mname) a JSON path is a dot separated sequence of \
414414+ indexing operations. For example $(b,books.[1].authors.[0]) indexes \
415415+ an object on the $(b,books) member, then on the second element of \
416416+ an array, then the $(b,authors) member of an object and finally \
417417+ the first element of that array. The root path is $(b,.), it can
418418+ be omitted if there are indexing operations.";
419419+ `P "In general because of your shell's special characters it's better \
420420+ to single quote your JSON paths.";
421421+ `P "Note that $(mname)'s JSON PATH are unrelated to the JSONPath \
422422+ query language (RFC 9535).";
423423+ `Blocks common_man; ]
424424+ in
425425+ Cmd.group (Cmd.info "jsont" ~version:"%%VERSION%%" ~doc ~sdocs ~exits ~man) @@
426426+ [get_cmd; delete_cmd; fmt_cmd; locs_cmd; set_cmd;]
427427+428428+let main () = Cmd.eval' jsont
429429+let () = if !Sys.interactive then () else exit (main ())
+42
vendor/opam/jsont/test/quickstart.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: CC0-1.0
44+ ---------------------------------------------------------------------------*)
55+66+(* Examples from the docs *)
77+88+let data =
99+{|{ "task": "Make new release",
1010+ "status": "todo",
1111+ "tags": ["work", "softwre"] }|}
1212+1313+let () =
1414+ let p = Jsont.Path.(root |> mem "tags" |> nth 1) in
1515+ let update = Jsont.(set_path string p "software") in
1616+ let correct = Jsont_bytesrw.recode_string ~layout:true update data in
1717+ print_endline (Result.get_ok correct)
1818+1919+module Status = struct
2020+ type t = Todo | Done | Cancelled
2121+ let assoc = ["todo", Todo; "done", Done; "cancelled", Cancelled ]
2222+ let jsont = Jsont.enum ~kind:"Status" assoc
2323+end
2424+2525+module Item = struct
2626+ type t = { task : string; status : Status.t; tags : string list; }
2727+ let make task status tags = { task; status; tags }
2828+ let task i = i.task
2929+ let status i = i.status
3030+ let tags i = i.tags
3131+ let jsont =
3232+ Jsont.Object.map ~kind:"Item" make
3333+ |> Jsont.Object.mem "task" Jsont.string ~enc:task
3434+ |> Jsont.Object.mem "status" Status.jsont ~enc:status
3535+ |> Jsont.Object.mem "tags" Jsont.(list string) ~enc:tags
3636+ ~dec_absent:[] ~enc_omit:(( = ) [])
3737+ |> Jsont.Object.finish
3838+end
3939+4040+let items = Jsont.list Item.jsont
4141+let items_of_json s = Jsont_bytesrw.decode_string items s
4242+let items_to_json ?format is = Jsont_bytesrw.encode_string ?format items is
+34
vendor/opam/jsont/test/test_brr.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open Brr
77+open B0_testing
88+99+(* Tests the common test suite with the Jsont_brr codec. *)
1010+1111+let error_to_string e = Jstr.to_string (Jv.Error.message e)
1212+1313+let decode ?layout t json =
1414+ Result.map_error error_to_string @@ Jsont_brr.decode t (Jstr.v json)
1515+1616+let encode ?format t v = match Jsont_brr.encode ?format t v with
1717+| Ok v -> Ok (Jstr.to_string v) | Error e -> Error (error_to_string e)
1818+1919+let test_funs = { Test_common.supports_layout = false; decode; encode }
2020+2121+let main () =
2222+ let exit = Test.main @@ fun () ->
2323+ Test_common.test_funs := test_funs;
2424+ Test_common.tests ();
2525+ in
2626+ let result = if exit = 0 then "All tests passed!" else "Some tests FAILED!" in
2727+ let children =
2828+ [ El.h1 [ El.txt' "Jsont_brr tests" ];
2929+ El.p [ El.txt' result];
3030+ El.p [ El.txt' "Open the browser console for details."] ]
3131+ in
3232+ El.set_children (Document.body G.document) children
3333+3434+let () = if !Sys.interactive then () else main ()
+37
vendor/opam/jsont/test/test_bytesrw.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open B0_std
77+open B0_testing
88+open Bytesrw
99+1010+(* Tests the common test suite with the Jsont_bytesrw codec. *)
1111+1212+let decode ?layout t json =
1313+ Jsont_bytesrw.decode_string ?layout ~locs:true t json
1414+1515+let encode ?format t v = Jsont_bytesrw.encode_string ?format t v
1616+let test_funs = { Test_common.supports_layout = true; decode; encode }
1717+1818+(* Other tests *)
1919+2020+let test_eod =
2121+ Test.test "Jsont_bytesrw.encode ~eod" @@ fun () ->
2222+ let b = Buffer.create 255 in
2323+ let w = Bytes.Writer.of_buffer b in
2424+ let () = Result.get_ok (Jsont_bytesrw.encode' Jsont.bool true ~eod:false w) in
2525+ let () = Result.get_ok (Jsont_bytesrw.encode' Jsont.bool true ~eod:true w) in
2626+ Test.string (Buffer.contents b) "truetrue";
2727+ Snap.raise (fun () -> Jsont_bytesrw.encode' Jsont.bool true ~eod:true w) @@
2828+ __POS_OF__ (Invalid_argument("slice written after eod"));
2929+ ()
3030+3131+let main () =
3232+ Test.main @@ fun () ->
3333+ Test_common.test_funs := test_funs;
3434+ Test.autorun ();
3535+ ()
3636+3737+let () = if !Sys.interactive then () else exit (main ())
+665
vendor/opam/jsont/test/test_common.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open B0_std
77+open B0_testing
88+open Test_common_samples
99+1010+let ( let* ) = Result.bind
1111+1212+(* This abstracts over codecs Jsont_brr, Jsont_bytesrw and Jsont.Json *)
1313+1414+type test_funs =
1515+ { supports_layout : bool;
1616+ decode : 'a. ?layout:bool -> 'a Jsont.t -> string -> ('a, string) result;
1717+ encode :
1818+ 'a. ?format:Jsont.format -> 'a Jsont.t -> 'a -> (string, string) result; }
1919+2020+let test_funs : test_funs ref =
2121+ ref { supports_layout = false;
2222+ decode = (fun ?layout _ _ -> assert false);
2323+ encode = (fun ?format _ _ -> assert false); }
2424+2525+let supports_layout () = !test_funs.supports_layout
2626+let decode ?layout t json = !test_funs.decode ?layout t json
2727+let encode ?format t v = !test_funs.encode ?format t v
2828+2929+(* Test combinators
3030+3131+ Note that the part of the test combinators rely on the library to
3232+ be correct. If something really feels fishy you may have to
3333+ investigate here too. *)
3434+3535+let decode_ok ?__POS__:pos ?value ?(eq = Test.T.any) t json =
3636+ Test.block ?__POS__:pos @@ fun () ->
3737+ match decode t json with
3838+ | Error e -> Test.fail "%a" Fmt.lines e ~__POS__
3939+ | Ok v' ->
4040+ match value with
4141+ | None -> ()
4242+ | Some value -> Test.eq eq v' value ~__POS__
4343+4444+let encode_ok ?__POS__:pos ?format t ~value json =
4545+ Test.block ?__POS__:pos @@ fun () ->
4646+ match encode ?format t value with
4747+ | Error e -> Test.fail "%a" Fmt.lines e ~__POS__
4848+ | Ok json' -> Test.string json' json ~__POS__
4949+5050+let decode_error ?__POS__:pos ?layout ?msg t json =
5151+ Test.block ?__POS__:pos @@ fun () ->
5252+ match decode ?layout t json with
5353+ | Ok _ -> Test.fail "Decode did not error" ~__POS__
5454+ | Error e ->
5555+ match msg with None -> () | Some msg -> Test.styled_string msg e ~__POS__
5656+5757+let encode_error ?__POS__:pos ?msg t v =
5858+ Test.block ?__POS__:pos @@ fun () ->
5959+ match encode t v with
6060+ | Ok _ -> Test.fail "Encode did not error" ~__POS__
6161+ | Error e ->
6262+ match msg with None -> () | Some msg -> Test.styled_string msg e ~__POS__
6363+6464+let update ?__POS__:pos ?(format = Jsont.Minify) q j j' =
6565+ let layout = format = Jsont.Layout in
6666+ Test.block ?__POS__:pos @@ fun () ->
6767+ match decode ~layout q j with
6868+ | Error e -> Test.fail "%a" Fmt.lines e ~__POS__
6969+ | Ok v when supports_layout () || not (format = Jsont.Layout) ->
7070+ encode_ok ~format Jsont.json ~value:v j' ~__POS__
7171+ | Ok v ->
7272+ let j' =
7373+ encode ~format:Jsont.Indent Jsont.json
7474+ (decode Jsont.json j' |> Result.get_ok)
7575+ |> Result.get_ok
7676+ in
7777+ encode_ok ~format:Jsont.Indent Jsont.json ~value:v j' ~__POS__
7878+7979+(* [trip t src] is the über testing combinator.
8080+8181+ It rounds trips a decode of [src] according to [t] and verifies
8282+ that the generated JSON [trip] has the same data unless [lossy] is
8383+ specified. If [value] is provided both decodes of [src] and [trip]
8484+ are tested against [value]. If [format] is specified with
8585+ [Jsont.Indent] or [Jsont.Layout] it assumes that [src] and [trip]
8686+ must be equal *)
8787+8888+let trip
8989+ ?(format = Jsont.Minify) ?(lossy = false) ?value ?(eq = Test.T.any)
9090+ ?__POS__:pos t src
9191+ =
9292+ Test.block ?__POS__:pos @@ fun () ->
9393+ let layout = format = Jsont.Layout in
9494+ let v =
9595+ Test.noraise ~__POS__ @@ fun () ->
9696+ Result.get_ok' (decode ~layout t src)
9797+ in
9898+ let trip =
9999+ Test.noraise ~__POS__ @@ fun () ->
100100+ Result.get_ok' (encode ~format t v)
101101+ in
102102+ let v' =
103103+ Test.noraise ~__POS__ @@ fun () ->
104104+ Result.get_ok' (decode t trip)
105105+ in
106106+ begin match value with
107107+ | None -> Test.eq eq v v' ~__POS__;
108108+ | Some value ->
109109+ Test.eq eq v value ~__POS__;
110110+ Test.eq eq v' value ~__POS__;
111111+ end;
112112+ if not lossy then begin
113113+ let json =
114114+ Test.noraise ~__POS__ @@ fun () ->
115115+ Result.get_ok' (decode Jsont.json src)
116116+ in
117117+ let trip =
118118+ Test.noraise ~__POS__ @@ fun () ->
119119+ Result.get_ok' (decode Jsont.json trip)
120120+ in
121121+ Test.eq (module Jsont.Json) json trip ~__POS__
122122+ end;
123123+ if format <> Jsont.Minify then begin
124124+ if format = Jsont.Layout && not (supports_layout ()) then () else
125125+ (* Test that src is a representation of the requested encoding format *)
126126+ Test.string src trip ~__POS__
127127+ end
128128+129129+let eq : (module Test.T with type t = 'a) = (module Jsont.Json)
130130+131131+(* Tests *)
132132+133133+let test_basic_invalid =
134134+ Test.test "basic invalid JSON" @@ fun () ->
135135+ decode_error Jsont.json "" ~__POS__;
136136+ decode_error (Jsont.null ()) "" ~__POS__;
137137+ decode_error Jsont.bool "" ~__POS__;
138138+ decode_error Jsont.json "ha" ~__POS__;
139139+ decode_error (Jsont.null ()) "ha" ~__POS__;
140140+ decode_error Jsont.bool "ha" ~__POS__;
141141+ decode_error Jsont.json " ha" ~__POS__;
142142+ decode_error Jsont.json " r6 " ~__POS__;
143143+ decode_error Jsont.json " { " ~__POS__;
144144+ decode_error Jsont.json " [ " ~__POS__;
145145+ decode_error Jsont.json " ][ " ~__POS__;
146146+ ()
147147+148148+let test_indent =
149149+ Test.test "Encode with indentation" @@ fun () ->
150150+ ()
151151+152152+let test_null =
153153+ Test.test "Jsont.null" @@ fun () ->
154154+ trip ~eq ~format:Layout Jsont.json " null \r\n" ~__POS__;
155155+ trip ~eq ~format:Layout Jsont.json "\n null " ~__POS__;
156156+ trip ~eq ~format:Layout Jsont.json "null" ~__POS__;
157157+ trip ~eq ~format:Indent Jsont.json "null" ~__POS__;
158158+ decode_error Jsont.json " nu " ~__POS__;
159159+ decode_error Jsont.json " nul " ~__POS__;
160160+ decode_error Jsont.json " n " ~__POS__;
161161+ trip (Jsont.null ()) " \n null \n " ~value:() ~__POS__;
162162+ trip (Jsont.null ()) " null " ~value:() ~__POS__;
163163+ decode_error (Jsont.null ()) " true " ~__POS__;
164164+ ()
165165+166166+let test_bool =
167167+ Test.test "Jsont.bool" @@ fun () ->
168168+ trip ~eq ~format:Layout Jsont.json " true \r\n" ~__POS__;
169169+ trip ~eq ~format:Layout Jsont.json "\n false " ~__POS__;
170170+ trip ~eq ~format:Layout Jsont.json "false" ~__POS__;
171171+ trip ~eq ~format:Indent Jsont.json "true" ~__POS__;
172172+ trip ~eq ~format:Indent Jsont.json "false" ~__POS__;
173173+ decode_error Jsont.json " fals " ~__POS__;
174174+ decode_error Jsont.json " falsee " ~__POS__;
175175+ decode_error Jsont.json " f " ~__POS__;
176176+ trip ~eq:Test.T.bool Jsont.bool " true \n " ~value:true ~__POS__;
177177+ trip ~eq:Test.T.bool Jsont.bool " false " ~value:false ~__POS__;
178178+ decode_error Jsont.bool " fals " ~__POS__;
179179+ ()
180180+181181+let test_numbers =
182182+ Test.test "Jsont.number" @@ fun () ->
183183+ trip ~eq ~format:Layout Jsont.json " 1 " ~__POS__;
184184+ trip ~eq ~format:Layout Jsont.json " 0 \n " ~__POS__;
185185+ trip ~eq ~format:Layout Jsont.json "\n 2.5 " ~__POS__;
186186+ trip ~eq ~format:Indent Jsont.json "0";
187187+ trip ~eq ~format:Indent Jsont.json "0.5";
188188+ decode_error Jsont.json " 01 " ~__POS__;
189189+ decode_error Jsont.json " -a " ~__POS__;
190190+ decode_error Jsont.json " 1. " ~__POS__;
191191+ decode_error Jsont.json " 1.0e+ " ~__POS__;
192192+ decode_error Jsont.json " inf " ~__POS__;
193193+ decode_error Jsont.json " infinity " ~__POS__;
194194+ decode_error Jsont.json " nan " ~__POS__;
195195+ let eq = Test.T.float in
196196+ trip ~eq Jsont.number " -0 " ~value:(-0.) ~__POS__;
197197+ trip ~eq Jsont.number " 0 " ~value:(0.) ~__POS__;
198198+ trip ~eq Jsont.number " 0E1 " ~value:0. ~__POS__;
199199+ trip ~eq Jsont.number " 0e+1 " ~value:0. ~__POS__;
200200+ trip ~eq Jsont.number " null " ~value:Float.nan ~__POS__;
201201+ encode_ok Jsont.number "null" ~value:Float.infinity ~__POS__;
202202+ encode_ok Jsont.number "null" ~value:Float.neg_infinity ~__POS__;
203203+ trip ~eq Jsont.number " 1e300 " ~value:1.e300 ~__POS__;
204204+ decode_error Jsont.number " fals " ~__POS__;
205205+ decode_error Jsont.number " 1. " ~__POS__;
206206+ decode_error Jsont.number " 1.0e+ " ~__POS__;
207207+ decode_error Jsont.number " 0E " ~__POS__;
208208+ decode_error Jsont.number " 1eE2 " ~__POS__;
209209+ ()
210210+211211+let test_strings =
212212+ Test.test "Jsont.string" @@ fun () ->
213213+ trip ~eq ~format:Layout Jsont.json {| "" |} ~__POS__;
214214+ trip ~eq ~format:Layout Jsont.json " \"\\\"\" " ~__POS__;
215215+ trip ~eq ~format:Layout Jsont.json " \"\\\\\" " ~__POS__;
216216+ trip ~eq ~format:Layout Jsont.json " \"hihi\" \n " ~__POS__;
217217+ trip ~eq ~format:Layout Jsont.json " \"hi\\nhi\" \n " ~__POS__;
218218+ if Sys.backend_type <> Sys.Other "js_of_ocaml" then begin
219219+ decode_error Jsont.json "\"\\uDC01\"" ~__POS__;
220220+ decode_error Jsont.json "\"\\uDBFF\"" ~__POS__;
221221+ decode_error Jsont.json "\"\\uDBFF\\uDBFF\"" ~__POS__;
222222+ end;
223223+ trip ~format:Indent Jsont.json {|""|};
224224+ trip ~format:Indent Jsont.json {|"blablabla"|};
225225+ decode_error Jsont.json "\"hi\nhi\"" ~__POS__;
226226+ decode_error Jsont.json "\n \"abla\" hi " ~__POS__;
227227+ decode_error Jsont.json "\n \"unclosed hi " ~__POS__;
228228+ trip ~eq:Test.T.string
229229+ Jsont.string "\"\\ud83D\\uDc2B\"" ~value:"🐫" ~__POS__;
230230+ trip ~eq:Test.T.string Jsont.string "\"🐫 a\"" ~value:"🐫 a" ~__POS__;
231231+ decode_error Jsont.string " false " ~__POS__;
232232+ decode_error Jsont.string "1.0" ~__POS__;
233233+ ()
234234+235235+let test_option =
236236+ Test.test "Jsont.{none,some,option}" @@ fun () ->
237237+ (* none *)
238238+ decode_error Jsont.none "2" ~__POS__;
239239+ decode_error Jsont.none "true" ~__POS__;
240240+ trip Jsont.none "null" ~value:None ~__POS__;
241241+ (* some *)
242242+ decode_error Jsont.(some bool) "null" ~__POS__;
243243+ decode_error Jsont.(some bool) "1.0" ~__POS__;
244244+ trip Jsont.(some bool) "true" ~value:(Some true) ~__POS__;
245245+ (* option *)
246246+ decode_error Jsont.(option bool) "1.0" ~__POS__;
247247+ decode_error Jsont.(option bool) "{}" ~__POS__;
248248+ trip Jsont.(option bool) "true" ~value:(Some true) ~__POS__;
249249+ trip Jsont.(option bool) "false" ~value:(Some false) ~__POS__;
250250+ trip Jsont.(option bool) "null" ~value:None ~__POS__;
251251+ ()
252252+253253+let test_ints =
254254+ Test.test "Jsont.{int…,uint…}" @@ fun () ->
255255+ (* uint8 *)
256256+ decode_error Jsont.uint8 "null" ~__POS__;
257257+ decode_error Jsont.uint8 "true" ~__POS__;
258258+ decode_error Jsont.uint8 "-1" ~__POS__;
259259+ decode_error Jsont.uint8 "256" ~__POS__;
260260+ trip Jsont.uint8 "0" ~value:0 ~__POS__;
261261+ trip Jsont.uint8 "255" ~value:255 ~__POS__;
262262+ (* uint16 *)
263263+ decode_error Jsont.uint16 "null" ~__POS__;
264264+ decode_error Jsont.uint16 "true" ~__POS__;
265265+ decode_error Jsont.uint16 "-1" ~__POS__;
266266+ decode_error Jsont.uint16 "65536" ~__POS__;
267267+ trip Jsont.uint16 "0" ~value:0 ~__POS__;
268268+ trip Jsont.uint16 "65535" ~value:65535 ~__POS__;
269269+ (* int8 *)
270270+ decode_error Jsont.int8 "null" ~__POS__;
271271+ decode_error Jsont.int8 "true" ~__POS__;
272272+ decode_error Jsont.int8 "-129" ~__POS__;
273273+ decode_error Jsont.int8 "128" ~__POS__;
274274+ trip Jsont.int8 "-128" ~value:(-128) ~__POS__;
275275+ trip Jsont.int8 "127" ~value:127 ~__POS__;
276276+ (* int32 *)
277277+ decode_error Jsont.int32 "null" ~__POS__;
278278+ decode_error Jsont.int32 "true" ~__POS__;
279279+ decode_error Jsont.int32 "-2147483649" ~__POS__;
280280+ decode_error Jsont.int32 "2147483648" ~__POS__;
281281+ trip Jsont.int32 "-2147483648" ~value:Int32.min_int ~__POS__;
282282+ trip Jsont.int32 "2147483647" ~value:Int32.max_int ~__POS__;
283283+ (* int64 *)
284284+ let max_exact = Int64.shift_left 1L 53 in
285285+ let max_exact_next = Int64.(add max_exact 1L) in
286286+ let min_exact = Int64.shift_left 1L 53 in
287287+ let min_exact_prev = Int64.(add max_exact 1L) in
288288+ decode_error Jsont.int64 "null" ~__POS__;
289289+ decode_error Jsont.int64 "true" ~__POS__;
290290+ trip Jsont.int64 (Fmt.str "%Ld" max_exact) ~value:max_exact ~__POS__;
291291+ trip Jsont.int64 (Fmt.str "%Ld" min_exact) ~value:min_exact ~__POS__;
292292+ trip Jsont.int64
293293+ (Fmt.str {|"%Ld"|} max_exact_next) ~value:max_exact_next ~__POS__;
294294+ trip Jsont.int64
295295+ (Fmt.str {|"%Ld"|} min_exact_prev) ~value:min_exact_prev ~__POS__;
296296+ (* int_as_string *)
297297+ trip Jsont.int_as_string {|"2"|} ~value:2 ~__POS__;
298298+ trip Jsont.int_as_string
299299+ (Fmt.str {|"%d"|} Int.max_int) ~value:Int.max_int ~__POS__;
300300+ trip Jsont.int_as_string
301301+ (Fmt.str {|"%d"|} Int.min_int) ~value:Int.min_int ~__POS__;
302302+ (* int64_as_string *)
303303+ trip Jsont.int64_as_string
304304+ (Fmt.str {|"%Ld"|} Int64.max_int) ~value:Int64.max_int ~__POS__;
305305+ trip Jsont.int64_as_string
306306+ (Fmt.str {|"%Ld"|} Int64.min_int) ~value:Int64.min_int ~__POS__;
307307+ ()
308308+309309+let test_floats =
310310+ Test.test "Jsont.{any_float,float_as_hex_string}" @@ fun () ->
311311+ (* any_float *)
312312+ let jsonstr f = Fmt.str {|"%s"|} (Float.to_string f) in
313313+ let eq = Test.T.float in
314314+ decode_ok ~eq Jsont.any_float "null" ~value:Float.nan ~__POS__;
315315+ trip ~eq Jsont.any_float " -0 " ~value:(-0.) ~__POS__;
316316+ trip ~eq Jsont.any_float " 0 " ~value:(0.) ~__POS__;
317317+ trip ~eq Jsont.any_float " 0.5 " ~value:0.5 ~__POS__;
318318+ decode_ok ~eq Jsont.any_float (jsonstr 0.5) ~value:0.5 ~__POS__;
319319+ trip ~eq Jsont.any_float
320320+ (jsonstr Float.nan) ~value:Float.nan ~__POS__;
321321+ trip ~eq Jsont.any_float
322322+ (jsonstr Float.infinity) ~value:Float.infinity ~__POS__;
323323+ trip ~eq Jsont.any_float
324324+ (jsonstr Float.neg_infinity) ~value:Float.neg_infinity ~__POS__;
325325+326326+ (* float_as_hex_string *)
327327+ let jsonstr f = Fmt.str {|"%h"|} f in
328328+ let t = Jsont.float_as_hex_string in
329329+ decode_error t "null" ~__POS__;
330330+ decode_error t "1.0" ~__POS__;
331331+ trip ~eq t (jsonstr 0.5) ~value:0.5 ~__POS__;
332332+ trip ~eq t (jsonstr Float.nan) ~value:Float.nan ~__POS__;
333333+ trip ~eq t (jsonstr Float.infinity) ~value:Float.infinity ~__POS__;
334334+ trip ~eq t (jsonstr Float.neg_infinity) ~value:Float.neg_infinity ~__POS__;
335335+ ()
336336+337337+let test_enum_and_binary_string =
338338+ Test.test "Jsont.{of_of_string,enum,binary_string}" @@ fun () ->
339339+ (* of_string *)
340340+ let int_of_string s = match int_of_string_opt s with
341341+ | None -> Error "Not an integer" | Some i -> Ok i
342342+ in
343343+ let t = Jsont.of_of_string ~kind:"int" int_of_string ~enc:Int.to_string in
344344+ trip ~eq:(Test.T.int) t {|"1"|} ~value:1 ~__POS__;
345345+ decode_error t {|"bla"|} ~__POS__;
346346+ (* enum *)
347347+ let enum = Jsont.enum ~kind:"heyho" ["hey", `Hey; "ho", `Ho ] in
348348+ decode_error enum {|null|} ~__POS__;
349349+ decode_error enum {|"ha"|} ~__POS__;
350350+ decode_error enum {|"farfarfar"|} ~__POS__;
351351+ trip enum {|"hey"|} ~value:`Hey ~__POS__;
352352+ trip enum {|"ho"|} ~value:`Ho ~__POS__;
353353+ (* binary_string *)
354354+ decode_error Jsont.binary_string {|null|};
355355+ decode_error Jsont.binary_string {|"00gabb"|} ~__POS__;
356356+ decode_error Jsont.binary_string {|"00aab"|} ~__POS__;
357357+ trip Jsont.binary_string {|"00a1bb"|} ~__POS__;
358358+ trip Jsont.binary_string {|"00a1ff"|} ~value:"\x00\xa1\xff" ~__POS__;
359359+ ()
360360+361361+let test_arrays =
362362+ Test.test "Jsont.{list,array,bigarray,t2,t3,t4,tn}" @@ fun () ->
363363+ let barr arr = Bigarray.Array1.of_array Int C_layout arr in
364364+ trip ~eq ~format:Layout Jsont.json " [] \n" ~__POS__;
365365+ trip ~eq ~format:Layout Jsont.json " [1, 3] \n\n" ~__POS__;
366366+ trip ~eq ~format:Layout Jsont.json " [1\n,3] \n\n" ~__POS__;
367367+ trip ~eq ~format:Layout Jsont.json " [1\n, \"a\",\n3 ] \n\n" ~__POS__;
368368+ trip ~eq ~format:Indent Jsont.json "[]" ~__POS__;
369369+ trip ~eq ~format:Indent Jsont.json "[\n 1\n]" ~__POS__;
370370+ trip ~eq ~format:Indent Jsont.json "[\n 1,\n \"bla\",\n 2\n]" ~__POS__;
371371+ decode_error Jsont.json "[1 ~__POS__;3]" ~__POS__;
372372+ decode_error Jsont.json " [1,3 " ~__POS__;
373373+ decode_error (Jsont.(list number)) "[1,true,3]" ~__POS__;
374374+ trip Jsont.(list int) " [ ] \n" ~value:[] ~__POS__;
375375+ trip Jsont.(list int) "[1,2,3]" ~value:[1;2;3] ~__POS__;
376376+ trip Jsont.(array int) " [ ] \n" ~value:[||] ~__POS__;
377377+ trip Jsont.(array int) "[1,2,3]" ~value:[|1;2;3|] ~__POS__;
378378+ trip Jsont.(bigarray Int int) " [ ] \n" ~value:(barr [||]) ~__POS__;
379379+ trip Jsont.(bigarray Int int) " [1,2,3] \n" ~value:(barr [|1;2;3;|]) ~__POS__;
380380+ let enc = Array.get in
381381+ let t2_int = Jsont.t2 ~dec:(fun x y -> [|x;y|]) ~enc Jsont.int in
382382+ decode_error t2_int "[]" ~__POS__;
383383+ decode_error t2_int "[1]" ~__POS__;
384384+ trip t2_int "[1,2]" ~value:[|1;2|] ~__POS__;
385385+ decode_error t2_int "[1,2,3]" ~__POS__;
386386+ let t3_int = Jsont.t3 ~dec:(fun x y z -> [|x;y;z|]) ~enc Jsont.int in
387387+ decode_error t3_int "[]" ~__POS__;
388388+ decode_error t3_int "[1]" ~__POS__;
389389+ decode_error t3_int "[1,2]" ~__POS__;
390390+ trip t3_int "[1,2,3]" ~value:[|1;2;3|] ~__POS__;
391391+ decode_error t3_int "[1,2,3,4]" ~__POS__;
392392+ let t4_int = Jsont.t4 ~dec:(fun x y z w -> [|x;y;z;w|]) ~enc Jsont.int in
393393+ decode_error t4_int "[]" ~__POS__;
394394+ decode_error t4_int "[1]" ~__POS__;
395395+ decode_error t4_int "[1,2]" ~__POS__;
396396+ decode_error t4_int "[1,2,3]" ~__POS__;
397397+ trip t4_int "[1,2,3,4]" ~value:[|1;2;3;4|] ~__POS__;
398398+ decode_error t4_int "[1,2,3,4,5]" ~__POS__;
399399+ let t0_int = Jsont.(tn ~n:0 int) in
400400+ let t2_int = Jsont.(tn ~n:2 int) in
401401+ trip t0_int "[]" ~value:[||] ~__POS__;
402402+ decode_error t0_int "[1]" ~__POS__;
403403+ decode_error t0_int "[1;2]" ~__POS__;
404404+ decode_error t2_int "[]" ~__POS__;
405405+ decode_error t2_int "[1]" ~__POS__;
406406+ trip t2_int "[1,2]" ~value:[|1;2|] ~__POS__;
407407+ decode_error t2_int "[1,2,3]" ~__POS__;
408408+ ()
409409+410410+let test_objects =
411411+ Test.test "Jsont.Object.map" @@ fun () ->
412412+ trip ~eq ~format:Layout Jsont.json " {} \n" ~__POS__;
413413+ trip ~eq ~format:Layout Jsont.json {| {"a": 1} |} ~__POS__;
414414+ trip ~eq ~format:Layout Jsont.json {| {"a": 1, "b":2} |} ~__POS__;
415415+ trip ~eq ~format:Indent Jsont.json "{}" ~__POS__;
416416+ trip ~eq ~format:Indent Jsont.json "{\n \"bla\": 1\n}";
417417+ trip ~format:Indent Item.jsont Item_data.i0_json ~value:Item_data.i0 ~__POS__;
418418+ trip ~format:Indent Item.jsont Item_data.i1_json ~value:Item_data.i1 ~__POS__;
419419+ ()
420420+421421+let test_unknown_mems =
422422+ Test.test "Jsont.Object.*_unknown" @@ fun () ->
423423+ (* Skip unknowns *)
424424+ trip Unknown.skip_jsont Unknown_data.u0 ~__POS__;
425425+ trip ~lossy:true Unknown.skip_jsont Unknown_data.u1 ~__POS__;
426426+ trip ~lossy:true Unknown.skip_jsont Unknown_data.u2 ~__POS__;
427427+ (* Error on unknown *)
428428+ trip Unknown.error_jsont Unknown_data.u0 ~__POS__;
429429+ decode_error Unknown.error_jsont Unknown_data.u1 ~__POS__;
430430+ decode_error Unknown.error_jsont Unknown_data.u2 ~__POS__;
431431+ (* Keep unknowns *)
432432+ trip Unknown.keep_jsont Unknown_data.u0 ~__POS__;
433433+ trip Unknown.keep_jsont Unknown_data.u1 ~__POS__;
434434+ trip Unknown.keep_jsont Unknown_data.u2 ~__POS__;
435435+ ()
436436+437437+let test_cases =
438438+ Test.test "Jsont.Object.Case" @@ fun () ->
439439+ decode_error Cases.Person_top.jsont Cases_data.invalid_miss ~__POS__;
440440+ decode_error Cases.Person_top.jsont Cases_data.invalid_case ~__POS__;
441441+ decode_error Cases.Person_field.jsont Cases_data.invalid_miss ~__POS__;
442442+ decode_error Cases.Person_field.jsont Cases_data.invalid_case ~__POS__;
443443+ trip Cases.Person_top.jsont Cases_data.author0
444444+ ~value:Cases_data.author0_top ~__POS__;
445445+ trip Cases.Person_top.jsont Cases_data.author0'
446446+ ~value:Cases_data.author0_top ~__POS__;
447447+ trip Cases.Person_top.jsont Cases_data.editor0
448448+ ~value:Cases_data.editor0_top ~__POS__;
449449+ trip Cases.Person_top.jsont Cases_data.editor0'
450450+ ~value:Cases_data.editor0_top ~__POS__;
451451+ trip Cases.Person_field.jsont Cases_data.author0
452452+ ~value:Cases_data.author0_field ~__POS__;
453453+ trip Cases.Person_field.jsont Cases_data.author0'
454454+ ~value:Cases_data.author0_field ~__POS__;
455455+ trip Cases.Person_field.jsont Cases_data.editor0
456456+ ~value:Cases_data.editor0_field ~__POS__;
457457+ trip Cases.Person_field.jsont Cases_data.editor0'
458458+ ~value:Cases_data.editor0_field ~__POS__;
459459+ (* Unknown value override *)
460460+ trip Cases.Keep_unknown.jsont ~eq:(module Cases.Keep_unknown)
461461+ Cases_data.unknown_a ~value:Cases_data.unknown_a_value ~__POS__;
462462+ trip Cases.Keep_unknown.jsont ~eq:(module Cases.Keep_unknown)
463463+ Cases_data.unknown_b ~value:Cases_data.unknown_b_value ~__POS__;
464464+ let module M = struct
465465+ type t = string String_map.t
466466+ let equal = String_map.equal String.equal
467467+ let pp ppf v = Fmt.string ppf "<value>"
468468+ end
469469+ in
470470+ trip Cases.Keep_unknown.a_jsont ~eq:(module M)
471471+ Cases_data.unknown_a ~value:Cases_data.unknown_a_a_value ~__POS__;
472472+ encode_ok Cases.Keep_unknown.jsont
473473+ ~format:Indent ~value:Cases_data.unknown_a_no_a_unknown_value
474474+ Cases_data.unknown_a_no_a_unknown;
475475+ ()
476476+477477+let test_rec =
478478+ Test.test "Jsont.rec" @@ fun () ->
479479+ let tree_null = Tree.jsont_with_null Jsont.int in
480480+ trip tree_null Tree_data.empty_null ~value:Tree_data.empty ~__POS__;
481481+ trip tree_null Tree_data.tree0_null ~value:Tree_data.tree0 ~__POS__;
482482+ let tree_cases = Tree.jsont_with_cases Jsont.int in
483483+ trip tree_cases Tree_data.empty_cases ~value:Tree_data.empty ~__POS__;
484484+ trip tree_cases Tree_data.tree0_cases ~value:Tree_data.tree0 ~__POS__;
485485+ ()
486486+487487+let test_zero =
488488+ Test.test "Jsont.zero" @@ fun () ->
489489+ let decode_ok = decode_ok ~eq:Test.T.unit in
490490+ decode_ok Jsont.zero "null" ~value:() ~__POS__;
491491+ decode_ok Jsont.zero "2" ~value:() ~__POS__;
492492+ decode_ok Jsont.zero {|"a"|} ~value:() ~__POS__;
493493+ decode_ok Jsont.zero {|[1]|} ~value:() ~__POS__;
494494+ decode_ok Jsont.zero {|{"bli":"bla"}|} ~value:() ~__POS__;
495495+ encode_ok Jsont.zero ~value:() "null" ~__POS__;
496496+ ()
497497+498498+let test_const =
499499+ Test.test "Jsont.const" @@ fun () ->
500500+ trip ~lossy:true Jsont.(const int 4) " {} " ~value:4 ~__POS__;
501501+ trip ~lossy:true Jsont.(const bool true) ~value:true "false" ~__POS__;
502502+ ()
503503+504504+let recode_int_to_string = Jsont.(recode ~dec:int string_of_int ~enc:string)
505505+506506+let test_array_queries =
507507+ let a = "[1,[ 1, 2], 3] " in
508508+ Test.test "Jsont.{nth,*_nth,filter_map_array,fold_array}" @@
509509+ fun () ->
510510+ (* Jsont.nth *)
511511+ decode_ok Jsont.(nth 0 @@ int) a ~value:1 ~__POS__;
512512+ decode_ok Jsont.(nth 1 @@ nth 1 int) a ~value:2 ~__POS__;
513513+ decode_ok Jsont.(nth 1 @@ list int) a ~value:[1;2] ~__POS__;
514514+ decode_error Jsont.(nth 3 @@ int) a ~__POS__;
515515+ decode_ok Jsont.(nth ~absent:3 3 @@ int) ~value:3 a ~__POS__;
516516+ decode_ok Jsont.(nth 0 @@ int) ~value:1 a ~__POS__;
517517+ decode_ok Jsont.(nth 1 @@ nth 1 int) a ~value:2 ~__POS__;
518518+ decode_ok Jsont.(nth 1 @@ list int) a ~value:[1;2] ~__POS__;
519519+ (* Jsont.{set,update}_nth} *)
520520+ update ~format:Jsont.Layout
521521+ Jsont.(update_nth 1 @@ update_nth 1 Jsont.(const int 4))
522522+ a "[1,[ 1, 4], 3] " ~__POS__;
523523+ update ~format:Jsont.Layout Jsont.(update_nth 1 @@ set_nth int 0 2) a
524524+ "[1,[ 2, 2], 3] " ~__POS__;
525525+ decode_error Jsont.(update_nth 1 @@ set_nth int 2 3) a;
526526+ decode_error Jsont.(update_nth 3 int) a;
527527+ update ~format:Jsont.Layout Jsont.(update_nth 3 ~absent:5 int) a
528528+ "[1,[ 1, 2], 3,5] ";
529529+ update ~format:Jsont.Layout
530530+ Jsont.(update_nth 1 @@ set_nth ~allow_absent:true int 3 3) a
531531+ "[1,[ 1, 2,0,3], 3] " ~__POS__;
532532+ update ~format:Jsont.Layout
533533+ Jsont.(update_nth 1 @@ set_nth
534534+ ~stub:(Jsont.Json.null ()) ~allow_absent:true int 3 3) a
535535+ "[1,[ 1, 2,null,3], 3] " ~__POS__;
536536+ update ~format:Jsont.Layout
537537+ Jsont.(update_nth 1 @@ update_nth 1 recode_int_to_string) a
538538+ "[1,[ 1, \"2\"], 3] " ~__POS__;
539539+ update Jsont.(update_nth 1 @@ delete_nth 0) a "[1,[2],3]" ~__POS__;
540540+ decode_ok
541541+ Jsont.(nth 1 @@ fold_array int (fun i v acc -> (i, v) :: acc) [])
542542+ a ~value:[(1,2); (0,1)] ~__POS__;
543543+ update Jsont.(update_nth 1 @@ filter_map_array int int
544544+ (fun _ v -> if v mod 2 = 0 then None else Some (v - 1)))
545545+ a "[1,[0],3]" ~__POS__;
546546+ (* Jsont.delete_nth *)
547547+ update ~format:Jsont.Layout Jsont.(delete_nth 1) a "[1, 3] " ~__POS__;
548548+ decode_error Jsont.(delete_nth 3) a ~__POS__;
549549+ update ~format:Jsont.Layout Jsont.(delete_nth ~allow_absent:true 3) a a
550550+ ~__POS__;
551551+ (* Jsont.filter_map_array *)
552552+ update ~format:Jsont.Layout
553553+ Jsont.(filter_map_array Jsont.json Jsont.json
554554+ (fun i v -> if i = 1 then None else Some v)) a
555555+ "[1, 3] " ~__POS__;
556556+ (* Jsont.fold_array *)
557557+ decode_ok Jsont.(nth 1 @@ fold_array int (fun i v acc -> i + v + acc) 0) a
558558+ ~value:4 ~__POS__;
559559+ ()
560560+561561+let test_object_queries =
562562+ Test.test "Jsont.{mem,*_mem,fold_object,filter_map_object}" @@ fun () ->
563563+ let o = {| { "a" : { "b" : 1 }, "c": 2 } |} in
564564+ (* Jsont.mem *)
565565+ decode_ok Jsont.(mem "a" @@ mem "b" int) o ~value:1 ~__POS__;
566566+ decode_error Jsont.(mem "a" @@ mem "c" int) o ~__POS__;
567567+ decode_ok Jsont.(mem "a" @@ mem ~absent:3 "c" int) o ~value:3 ~__POS__;
568568+ (* Jsont.{update,set}_mem *)
569569+ update ~format:Jsont.Layout
570570+ Jsont.(update_mem "a" @@ update_mem "b" (const int 3))
571571+ o {| { "a" : { "b" : 3 }, "c": 2 } |} ~__POS__;
572572+ update ~format:Jsont.Layout
573573+ Jsont.(update_mem "a" @@ update_mem "b" recode_int_to_string)
574574+ o {| { "a" : { "b" : "1" }, "c": 2 } |} ~__POS__;
575575+ decode_error
576576+ Jsont.(update_mem "a" @@ update_mem "c" (const int 4)) o ~__POS__;
577577+ update ~format:Jsont.Layout
578578+ Jsont.(update_mem "a" @@ update_mem "c" ~absent:4 (const int 5)) o
579579+ {| { "a" : { "b" : 1 ,"c":5}, "c": 2 } |} ~__POS__;
580580+ update ~format:Jsont.Layout
581581+ Jsont.(set_mem int "a" 2) o
582582+ {| { "a" : 2, "c": 2 } |} ~__POS__;
583583+ decode_error Jsont.(set_mem int "d" 2) o ~__POS__;
584584+ update ~format:Jsont.Layout Jsont.(set_mem ~allow_absent:true int "d" 3) o
585585+ {| { "a" : { "b" : 1 }, "c": 2 ,"d":3} |} ~__POS__;
586586+ (* Jsont.delete_mem *)
587587+ decode_error Jsont.(update_mem "a" @@ delete_mem "c") o ~__POS__;
588588+ update ~format:Jsont.Layout
589589+ Jsont.(update_mem "a" @@ delete_mem ~allow_absent:true "c")
590590+ o o ~__POS__;
591591+ update ~format:Jsont.Layout Jsont.(update_mem "a" @@ delete_mem "b")
592592+ o {| { "a" : {}, "c": 2 } |} ~__POS__;
593593+ update ~format:Jsont.Layout Jsont.(delete_mem "a")
594594+ o {| { "c": 2 } |} ~__POS__;
595595+ (* Jsont.filter_map_object *)
596596+ update ~format:Jsont.Layout
597597+ Jsont.(filter_map_object Jsont.json Jsont.json
598598+ (fun m n v -> if n = "a" then None else Some ((n, m), v)))
599599+ o {| { "c": 2 } |} ~__POS__;
600600+ (* Jsont.fold *)
601601+ decode_ok Jsont.(mem "a" @@
602602+ fold_object int (fun _ n i acc -> i + acc) 2)
603603+ o ~value:3 ~__POS__;
604604+ ()
605605+606606+let test_path_queries =
607607+ Test.test "Jsont.{path,*_path}" @@ fun () ->
608608+ let v = {| [ 0, { "a": 1}, 2 ] |} in
609609+ (* Jsont.path *)
610610+ decode_error Jsont.(path Path.root int) v ~__POS__;
611611+ update ~format:Jsont.Layout Jsont.(path Path.root Jsont.json) v v ~__POS__;
612612+ decode_ok Jsont.(path Path.(root |> nth 1 |> mem "a") int) v ~value:1;
613613+ decode_ok Jsont.(path Path.(root |> nth 1 |> mem "b") ~absent:2 int) v
614614+ ~value:2 ~__POS__;
615615+ (* Jsont.{set,update}_path} *)
616616+ update ~format:Jsont.Layout Jsont.(set_path int Path.root 2)
617617+ v {|2|} ~__POS__;
618618+ update ~format:Jsont.Layout
619619+ Jsont.(set_path string Path.(root |> nth 1 |> mem "a") "hey")
620620+ v {| [ 0, { "a": "hey"}, 2 ] |} ~__POS__;
621621+ update ~format:Jsont.Layout
622622+ Jsont.(set_path ~allow_absent:true
623623+ string Path.(root |> nth 1 |> mem "b") "hey")
624624+ v {| [ 0, { "a": 1,"b":"hey"}, 2 ] |} ~__POS__;
625625+ update ~format:Jsont.Layout
626626+ Jsont.(update_path Path.(root |> nth 1 |> mem "a")
627627+ (map int ~dec:succ ~enc:Fun.id))
628628+ v {| [ 0, { "a": 2}, 2 ] |} ~__POS__;
629629+ (* Jsont.delete_path *)
630630+ update ~format:Jsont.Layout
631631+ Jsont.(delete_path Path.(root |> nth 1 |> mem "a")) v
632632+ {| [ 0, {}, 2 ] |} ~__POS__;
633633+ update ~format:Jsont.Layout
634634+ Jsont.(delete_path Path.(root |> nth 1)) v
635635+ {| [ 0, 2 ] |} ~__POS__;
636636+ update ~format:Jsont.Layout
637637+ Jsont.(delete_path Path.root) v
638638+ {|null|} ~__POS__;
639639+ decode_error Jsont.(delete_path Path.(root |> nth 1 |> mem "b")) v ~__POS__;
640640+ update ~format:Jsont.Layout
641641+ Jsont.(delete_path ~allow_absent:true Path.(root |> nth 1 |> mem "b"))
642642+ v v ~__POS__;
643643+ ()
644644+645645+let tests () =
646646+ test_basic_invalid ();
647647+ test_null ();
648648+ test_bool ();
649649+ test_numbers ();
650650+ test_strings ();
651651+ test_option ();
652652+ test_ints ();
653653+ test_floats ();
654654+ test_enum_and_binary_string ();
655655+ test_arrays ();
656656+ test_objects ();
657657+ test_unknown_mems ();
658658+ test_cases ();
659659+ test_rec ();
660660+ test_zero ();
661661+ test_const ();
662662+ test_array_queries ();
663663+ test_object_queries ();
664664+ test_path_queries ();
665665+ ()
+414
vendor/opam/jsont/test/test_common_samples.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+77+module String_map = Map.Make (String)
88+99+(* Items to do. *)
1010+1111+module Status = struct
1212+ type t = Todo | Done | Cancelled
1313+ let assoc = ["todo", Todo; "done", Done; "cancelled", Cancelled ]
1414+ let jsont = Jsont.enum ~kind:"Status" assoc
1515+end
1616+1717+module Item = struct
1818+ type t = { task : string; status : Status.t; tags : string list; }
1919+ let make task status tags = { task; status; tags }
2020+ let task i = i.task
2121+ let status i = i.status
2222+ let tags i = i.tags
2323+ let jsont =
2424+ Jsont.Object.map ~kind:"Item" make
2525+ |> Jsont.Object.mem "task" Jsont.string ~enc:task
2626+ |> Jsont.Object.mem "status" Status.jsont ~enc:status
2727+ |> Jsont.Object.mem "tags"
2828+ Jsont.(list string) ~dec_absent:[] ~enc:tags ~enc_omit:(( = ) [])
2929+ |> Jsont.Object.finish
3030+3131+end
3232+3333+module Item_data = struct
3434+ let i0 = Item.{ task = "Hey"; status = Todo; tags = ["huhu";"haha"] }
3535+ let i0_json = (* in Jsont.Indent format *)
3636+ "{\n\
3737+ \ \"task\": \"Hey\",\n\
3838+ \ \"status\": \"todo\",\n\
3939+ \ \"tags\": [\n\
4040+ \ \"huhu\",\n\
4141+ \ \"haha\"\n\
4242+ \ ]\n\
4343+ }"
4444+4545+ let i1 = Item.{ task = "Ho"; status = Done; tags = [] }
4646+ let i1_json = (* in Jsont.Indent format *)
4747+ "{\n\
4848+ \ \"task\": \"Ho\",\n\
4949+ \ \"status\": \"done\"\n\
5050+ }"
5151+end
5252+5353+(* JSON types to excerice the different unknown member behaviours. *)
5454+5555+module Unknown = struct
5656+ type t = { m : bool }
5757+ let make m = { m }
5858+ let m v = v.m
5959+6060+ let skip_jsont =
6161+ Jsont.Object.map ~kind:"unknown-skip" make
6262+ |> Jsont.Object.mem "m" Jsont.bool ~enc:m
6363+ |> Jsont.Object.skip_unknown
6464+ |> Jsont.Object.finish
6565+6666+ let error_jsont =
6767+ Jsont.Object.map ~kind:"unknown-skip" make
6868+ |> Jsont.Object.mem "m" Jsont.bool ~enc:m
6969+ |> Jsont.Object.error_unknown
7070+ |> Jsont.Object.finish
7171+7272+ let keep_jsont : (t * int String_map.t) Jsont.t =
7373+ let unknown = Jsont.Object.Mems.string_map Jsont.int in
7474+ Jsont.Object.map ~kind:"unknown-keep" (fun m imap -> make m, imap)
7575+ |> Jsont.Object.mem "m" Jsont.bool ~enc:(fun (v, _) -> m v)
7676+ |> Jsont.Object.keep_unknown unknown ~enc:snd
7777+ |> Jsont.Object.finish
7878+end
7979+8080+module Unknown_data = struct
8181+ let u0 = {| { "m": true } |}
8282+ let u1 = {| { "m": true, "u0": 0, "u1": 1 } |}
8383+ let u2 = {| { "u": 0, "m": true } |}
8484+end
8585+8686+(* Object cases *)
8787+8888+module Cases = struct
8989+ (* There are two ways to encode object cases in OCaml, either as a toplevel
9090+ variant or as a record with a field that is a variant. With the design
9191+ we have the encoding is mostly the same. This is the JSON we deal with:
9292+9393+ { "type": "author",
9494+ "name": "…",
9595+ "pseudo": "…",
9696+ "book_count": 1 }
9797+9898+ { "type": "editor",
9999+ "name": "…",
100100+ "publisher": "…" } *)
101101+102102+ module Person_top = struct (* Toplevel variant *)
103103+ module Author = struct
104104+ type t = { name : string; pseudo : string; book_count : int; }
105105+ let make name book_count pseudo = { name; pseudo; book_count }
106106+ let name a = a.name
107107+ let book_count a = a.book_count
108108+ let pseudo a = a.pseudo
109109+ let jsont =
110110+ Jsont.Object.map ~kind:"Author" make
111111+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
112112+ |> Jsont.Object.mem "book_count" Jsont.int ~enc:book_count
113113+ |> Jsont.Object.mem "pseudo" Jsont.string ~enc:pseudo
114114+ |> Jsont.Object.finish
115115+ end
116116+117117+ module Editor = struct
118118+ type t = { name : string; publisher : string }
119119+ let make name publisher = { name; publisher}
120120+ let name e = e.name
121121+ let publisher e = e.publisher
122122+ let jsont =
123123+ Jsont.Object.map ~kind:"Editor" make
124124+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
125125+ |> Jsont.Object.mem "publisher" Jsont.string ~enc:publisher
126126+ |> Jsont.Object.finish
127127+ end
128128+129129+ type t = Author of Author.t | Editor of Editor.t
130130+131131+ let author a = Author a
132132+ let editor e = Editor e
133133+134134+ let jsont =
135135+ let case_a = Jsont.Object.Case.map "author" Author.jsont ~dec:author in
136136+ let case_e = Jsont.Object.Case.map "editor" Editor.jsont ~dec:editor in
137137+ let cases = Jsont.Object.Case.[make case_a; make case_e] in
138138+ let enc_case = function
139139+ | Author a -> Jsont.Object.Case.value case_a a
140140+ | Editor e -> Jsont.Object.Case.value case_e e
141141+ in
142142+ Jsont.Object.map ~kind:"Person" Fun.id
143143+ |> Jsont.Object.case_mem "type"
144144+ Jsont.string ~tag_to_string:Fun.id ~enc:Fun.id ~enc_case cases
145145+ |> Jsont.Object.finish
146146+ end
147147+148148+ module Person_field = struct (* Variant in a field *)
149149+ type author = { pseudo : string; book_count : int }
150150+ let make_author pseudo book_count = { pseudo; book_count }
151151+ let pseudo a = a.pseudo
152152+ let book_count a = a.book_count
153153+ let author_jsont =
154154+ Jsont.Object.map ~kind:"Author" make_author
155155+ |> Jsont.Object.mem "pseudo" Jsont.string ~enc:pseudo
156156+ |> Jsont.Object.mem "book_count" Jsont.int ~enc:book_count
157157+ |> Jsont.Object.finish
158158+159159+ type editor = { publisher : string; }
160160+ let make_editor publisher = { publisher }
161161+ let publisher e = e.publisher
162162+ let editor_jsont =
163163+ Jsont.Object.map ~kind:"Editor" make_editor
164164+ |> Jsont.Object.mem "publisher" Jsont.string ~enc:publisher
165165+ |> Jsont.Object.finish
166166+167167+ type type' = Author of author | Editor of editor
168168+ let author a = Author a
169169+ let editor e = Editor e
170170+171171+ type t = { type' : type'; name : string }
172172+ let make type' name = { type'; name }
173173+ let type' v = v.type'
174174+ let name v = v.name
175175+176176+ let jsont =
177177+ let case_a = Jsont.Object.Case.map "author" author_jsont ~dec:author in
178178+ let case_e = Jsont.Object.Case.map "editor" editor_jsont ~dec:editor in
179179+ let cases = Jsont.Object.Case.[make case_a; make case_e] in
180180+ let enc_case = function
181181+ | Author a -> Jsont.Object.Case.value case_a a
182182+ | Editor e -> Jsont.Object.Case.value case_e e
183183+ in
184184+ Jsont.Object.map ~kind:"Person" make
185185+ |> Jsont.Object.case_mem "type"
186186+ ~tag_to_string:Fun.id Jsont.string ~enc:type' ~enc_case cases
187187+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
188188+ |> Jsont.Object.finish
189189+ end
190190+191191+ module Keep_unknown = struct
192192+ type a = string String_map.t
193193+ let a_jsont =
194194+ let unknown = Jsont.Object.Mems.string_map Jsont.string in
195195+ Jsont.Object.map ~kind:"A" Fun.id
196196+ |> Jsont.Object.keep_unknown unknown ~enc:Fun.id
197197+ |> Jsont.Object.finish
198198+199199+ type b = { name : string }
200200+ let name b = b.name
201201+ let b_jsont =
202202+ Jsont.Object.map ~kind:"B" (fun name -> { name })
203203+ |> Jsont.Object.mem "name" Jsont.string ~enc:name
204204+ |> Jsont.Object.error_unknown
205205+ |> Jsont.Object.finish
206206+207207+ type type' = A of a | B of b
208208+ let a a = A a
209209+ let b b = B b
210210+ type t = { type' : type'; unknown : Jsont.json }
211211+ let make type' unknown = { type'; unknown }
212212+ let type' v = v.type'
213213+ let unknown v = v.unknown
214214+ let equal v0 v1 = match v0.type', v1.type' with
215215+ | A a0, A a1 ->
216216+ String_map.equal String.equal a0 a1 &&
217217+ Jsont.Json.equal v0.unknown v1.unknown
218218+ | B b0, B b1 ->
219219+ String.equal b0.name b1.name &&
220220+ Jsont.Json.equal v0.unknown v1.unknown
221221+ | _, _ -> false
222222+223223+ let pp ppf v = B0_std.Fmt.string ppf "<value>"
224224+225225+ let jsont =
226226+ let case_a = Jsont.Object.Case.map "A" a_jsont ~dec:a in
227227+ let case_b = Jsont.Object.Case.map "B" b_jsont ~dec:b in
228228+ let cases = Jsont.Object.Case.[make case_a; make case_b] in
229229+ let enc_case = function
230230+ | A a -> Jsont.Object.Case.value case_a a
231231+ | B b -> Jsont.Object.Case.value case_b b
232232+ in
233233+ Jsont.Object.map ~kind:"Keep_unknown" make
234234+ |> Jsont.Object.case_mem "type"
235235+ ~tag_to_string:Fun.id Jsont.string ~enc:type' ~enc_case cases
236236+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
237237+ |> Jsont.Object.finish
238238+ end
239239+240240+end
241241+242242+module Cases_data = struct
243243+ let author0_top, author0_field =
244244+ let name = "Jane" and book_count = 2 and pseudo = "Jude" in
245245+ Cases.Person_top.Author { name; book_count; pseudo },
246246+ { Cases.Person_field.type' = Author { book_count; pseudo }; name }
247247+248248+ let invalid_miss = (* Missing type field. *)
249249+ {| { "name": "Jane", "tope": "ha", "tape": "ha",
250250+ "book_count": 2, "pseudo": "Jude" }|}
251251+252252+ let invalid_case =
253253+ {| { "type": "reader", "name": "Jane" }|}
254254+255255+ let author0 =
256256+ {| { "type": "author", "name": "Jane", "book_count": 2, "pseudo": "Jude" }|}
257257+258258+ let author0' = (* out of order case field in the middle *)
259259+ {| { "name": "Jane", "book_count": 2, "type": "author", "pseudo": "Jude" }|}
260260+261261+ let editor0_top, editor0_field =
262262+ let name = "Joe" and publisher = "Red books" in
263263+ Cases.Person_top.Editor { name; publisher },
264264+ { Cases.Person_field.type' = Editor { publisher }; name }
265265+266266+ let editor0 =
267267+ {| { "type": "editor", "name": "Joe", "publisher": "Red books" } |}
268268+269269+ let editor0' = (* out of order case field at the end *)
270270+ {| { "name": "Joe", "publisher": "Red books", "type": "editor" } |}
271271+272272+ let unknown_a =
273273+ {| { "m1": "n", "type": "A", "m0": "o" } |}
274274+275275+ let unknown_b =
276276+ {| { "type": "B", "m1": "v1", "name": "ha", "m2": 0 } |}
277277+278278+ let unknown_a_value =
279279+ let unknown =
280280+ Jsont.Json.(object' [mem (name "m0") (string "o");
281281+ mem (name "m1") (string "n")])
282282+ in
283283+ Cases.Keep_unknown.make (A String_map.empty) unknown
284284+285285+ let unknown_a_a_value =
286286+ String_map.empty
287287+ |> String_map.add "m0" "o"
288288+ |> String_map.add "m1" "n"
289289+ |> String_map.add "type" "A"
290290+291291+ let unknown_a_no_a_unknown = "{\n \"type\": \"A\"\n}"
292292+ let unknown_a_no_a_unknown_value =
293293+ (* Since the map should be ignored since the case object overides it *)
294294+ let unknown = Jsont.Json.object' [] in
295295+ Cases.Keep_unknown.make (A String_map.(empty |> add "bli" "bla")) unknown
296296+297297+ let unknown_b_value =
298298+ let unknown =
299299+ Jsont.Json.(object' [mem (name "m1") (string "v1");
300300+ mem (name "m2") (number 0.0)])
301301+ in
302302+ Cases.Keep_unknown.make (B { name = "ha" }) unknown
303303+end
304304+305305+(* Type recursion *)
306306+307307+module Tree = struct
308308+ type 'a tree = Empty | Node of 'a tree * 'a * 'a tree
309309+310310+ let rec pp pp_v ppf = function
311311+ | Empty -> Format.fprintf ppf "Empty"
312312+ | Node (l, v, r) ->
313313+ Format.fprintf ppf "@[Node @[<1>(%a,@ %a,@ %a)@]@]"
314314+ (pp pp_v) l pp_v v (pp pp_v) r
315315+316316+ (* Encoded with null for Empty and nodes with:
317317+318318+ { "left": …,
319319+ "value": …,
320320+ "right": … }
321321+322322+ and null is used for empty. *)
323323+ let jsont_with_null t =
324324+ let rec tree = lazy begin
325325+ let empty = Jsont.null Empty in
326326+ let node =
327327+ let not_a_node () = failwith "not a node" in
328328+ let value = function Node (_, v, _) -> v | _ -> not_a_node () in
329329+ let left = function Node (l, _, _) -> l | _ -> not_a_node () in
330330+ let right = function Node (_, _, r) -> r | _ -> not_a_node () in
331331+ Jsont.Object.map ~kind:"node" (fun l v r -> Node (l, v, r))
332332+ |> Jsont.Object.mem ~enc:left "left" (Jsont.rec' tree)
333333+ |> Jsont.Object.mem ~enc:value "value" t
334334+ |> Jsont.Object.mem ~enc:right "right" (Jsont.rec' tree)
335335+ |> Jsont.Object.finish
336336+ in
337337+ let enc = function Empty -> empty | Node _ -> node in
338338+ Jsont.any ~kind:"tree" ~dec_null:empty ~dec_object:node ~enc ()
339339+ end
340340+ in
341341+ Lazy.force tree
342342+343343+ (* Encoded as two cases :
344344+345345+ { "type": "empty" }
346346+347347+ { "type": "node",
348348+ "left": …,
349349+ "value": …,
350350+ "right": … } *)
351351+352352+ let jsont_with_cases t =
353353+ let rec tree = lazy begin
354354+ let leaf_jsont = Jsont.Object.map Empty |> Jsont.Object.finish in
355355+ let node_jsont =
356356+ let not_a_node () = failwith "not a node" in
357357+ let value = function Node (_, v, _) -> v | _ -> not_a_node () in
358358+ let left = function Node (l, _, _) -> l | _ -> not_a_node () in
359359+ let right = function Node (_, _, r) -> r | _ -> not_a_node () in
360360+ Jsont.Object.map (fun l v r -> Node (l, v, r))
361361+ |> Jsont.Object.mem ~enc:left "left" (Jsont.rec' tree)
362362+ |> Jsont.Object.mem ~enc:value "value" t
363363+ |> Jsont.Object.mem ~enc:right "right" (Jsont.rec' tree)
364364+ |> Jsont.Object.finish
365365+ in
366366+ let case_leaf = Jsont.Object.Case.map "empty" leaf_jsont ~dec:Fun.id in
367367+ let case_node = Jsont.Object.Case.map "node" node_jsont ~dec:Fun.id in
368368+ let enc_case = function
369369+ | Empty as v -> Jsont.Object.Case.value case_leaf v
370370+ | Node _ as v -> Jsont.Object.Case.value case_node v
371371+ in
372372+ let cases = Jsont.Object.Case.[ make case_leaf; make case_node ] in
373373+ Jsont.Object.map ~kind:"tree" Fun.id
374374+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:Fun.id ~enc_case cases
375375+ |> Jsont.Object.finish
376376+ end
377377+ in
378378+ Lazy.force tree
379379+380380+end
381381+382382+module Tree_data = struct
383383+ let empty = Tree.Empty
384384+ let empty_null = {| null |}
385385+ let empty_cases = {| { "type": "empty" } |}
386386+387387+ let tree0 = Tree.Node (Node (Node (Empty, 1, Empty),
388388+ 2,
389389+ Empty),
390390+ 3,
391391+ Node (Empty, 4, Empty))
392392+393393+ let tree0_null =
394394+ {| { "left": { "left": { "left": null, "value": 1, "right": null },
395395+ "value": 2,
396396+ "right": null },
397397+ "value": 3,
398398+ "right": { "left": null, "value": 4, "right": null } } |}
399399+400400+ let tree0_cases = (* Case member not in order to check decode delays. *)
401401+ {| { "left": { "type": "node",
402402+ "left": { "type": "node",
403403+ "left": { "type": "empty" },
404404+ "right": { "type": "empty" },
405405+ "value": 1 },
406406+ "value": 2,
407407+ "right": { "type" : "empty" }},
408408+ "value": 3,
409409+ "type": "node",
410410+ "right": { "type": "node",
411411+ "left": { "type" : "empty" },
412412+ "value": 4,
413413+ "right": { "type" : "empty" }}} |}
414414+end
+34
vendor/opam/jsont/test/test_json.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+open B0_std
77+open B0_testing
88+99+(* Tests the common test suite with the Jsont.Json codec. *)
1010+1111+(* Since the Jsont.Json codec works only on Jsont.json values we use
1212+ Jsont_bytesrw to codec JSON to Jsont.json values and then apply the
1313+ Jsont.Json codec. So the tests rely on a working Jsont_bytesrw
1414+ codec *)
1515+1616+let decode ?layout t json =
1717+ match Jsont_bytesrw.decode_string ?layout ~locs:true Jsont.json json with
1818+ | Error _ as e -> e
1919+ | Ok json -> Jsont.Json.decode t json
2020+2121+let encode ?format t v =
2222+ match Jsont.Json.encode t v with
2323+ | Error _ as e -> e
2424+ | Ok json -> Jsont_bytesrw.encode_string ?format Jsont.json json
2525+2626+let test_funs = { Test_common.supports_layout = true; decode; encode }
2727+2828+let main () =
2929+ Test.main @@ fun () ->
3030+ Test_common.test_funs := test_funs;
3131+ Test_common.tests ();
3232+ ()
3333+3434+let () = if !Sys.interactive then () else exit (main ())
+69
vendor/opam/jsont/test/test_seriot_suite.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* Runs the codec on https://github.com/nst/JSONTestSuite *)
77+88+open B0_std
99+open B0_testing
1010+open Result.Syntax
1111+1212+let status_of_filename name =
1313+ if String.starts_with ~prefix:"y_" name then `Accept else
1414+ if String.starts_with ~prefix:"n_" name then `Reject else
1515+ if String.starts_with ~prefix:"i_" name then `Indeterminate else
1616+ Test.failstop "Unknown kind of test: %s" name
1717+1818+let test ~show_errors file =
1919+ let name = Fpath.basename file in
2020+ Test.test name @@ fun () ->
2121+ Test.noraise ~__POS__ @@ fun () ->
2222+ Result.get_ok' @@
2323+ let* json = Os.File.read file in
2424+ let status = status_of_filename name in
2525+ let file = Fpath.to_string file in
2626+ match Jsont_bytesrw.decode_string ~file ~locs:true Jsont.json json with
2727+ | Ok _ ->
2828+ if status = `Accept || status = `Indeterminate
2929+ then Ok ()
3030+ else (Test.failstop " @[<v>Should have been rejected:@,%s@]" json)
3131+ | Error e ->
3232+ if show_errors then Log.err (fun m -> m "%s" e);
3333+ if status = `Reject || status = `Indeterminate
3434+ then Ok ()
3535+ else (Test.failstop " @[<v>Should have been accepted:@,%s@]" json)
3636+3737+let run ~dir ~show_errors =
3838+ let dir = Fpath.v dir in
3939+ Log.if_error ~use:1 @@
4040+ let* exists = Os.Dir.exists dir in
4141+ if not exists
4242+ then begin
4343+ Fmt.pr "@[%a @[<v>JSONTestSuite not found@,\
4444+ Use %a to download it@]@]" Test.Fmt.skip ()
4545+ Fmt.code "b0 -- download-seriot-suite";
4646+ Ok 0
4747+ end else
4848+ let dir = Fpath.(dir / "test_parsing") in
4949+ let* files = Os.Dir.fold_files ~recurse:false Os.Dir.path_list dir [] in
5050+ Result.ok @@ Test.main @@ fun () ->
5151+ List.iter (fun file -> test ~show_errors file ()) files
5252+5353+open Cmdliner
5454+open Cmdliner.Term.Syntax
5555+5656+let cmd =
5757+ let doc = "Run Nicolas Seriot's JSON test suite" in
5858+ Cmd.v (Cmd.info "test_seriot_suite" ~doc) @@
5959+ let+ show_errors =
6060+ let doc = "Show errors" in
6161+ Arg.(value & flag & info ["e"; "show-errors"] ~doc)
6262+ and+ dir =
6363+ let doc = "Repository directory of the test suite." in
6464+ Arg.(value & pos 0 dir "tmp/JSONTestSuite" & info [] ~doc ~docv:"REPO")
6565+ in
6666+ run ~dir ~show_errors
6767+6868+let main () = Cmd.eval' cmd
6969+let () = if !Sys.interactive then () else exit (main ())
+283
vendor/opam/jsont/test/topojson.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(* Topojson codec https://github.com/topojson/topojson-specification *)
77+88+module String_map = Map.Make (String)
99+1010+module Position = struct
1111+ type t = float array
1212+ let jsont = Jsont.(array ~kind:"Position" number)
1313+end
1414+1515+module Bbox = struct
1616+ type t = float array
1717+ let jsont = Jsont.(array ~kind:"Bbox" number)
1818+end
1919+2020+module Arcs = struct
2121+ type t = Position.t array array
2222+ let jsont = Jsont.(array ~kind:"Arcs" (array Position.jsont))
2323+end
2424+2525+module Transform = struct
2626+ type v2 = float * float
2727+ type t = { scale : v2; translate : v2 }
2828+2929+ let make scale translate = { scale; translate }
3030+ let scale t = t.scale
3131+ let translate t = t.translate
3232+3333+ let v2_jsont =
3434+ let dec x y = x, y in
3535+ let enc (x, y) i = if i = 0 then x else y in
3636+ Jsont.t2 ~dec ~enc Jsont.number
3737+3838+ let jsont =
3939+ Jsont.Object.map ~kind:"Transform" make
4040+ |> Jsont.Object.mem "scale" v2_jsont ~enc:scale
4141+ |> Jsont.Object.mem "translate" v2_jsont ~enc:translate
4242+ |> Jsont.Object.finish
4343+end
4444+4545+module Point = struct
4646+ type t = { coordinates : Position.t }
4747+ let make coordinates = { coordinates }
4848+ let coordinates v = v.coordinates
4949+ let jsont =
5050+ Jsont.Object.map ~kind:"Point" make
5151+ |> Jsont.Object.mem "coordinates" Position.jsont ~enc:coordinates
5252+ |> Jsont.Object.finish
5353+end
5454+5555+module Multi_point = struct
5656+ type t = { coordinates : Position.t list }
5757+ let make coordinates = { coordinates }
5858+ let coordinates v = v.coordinates
5959+ let jsont =
6060+ Jsont.Object.map ~kind:"MultiPoint" make
6161+ |> Jsont.Object.mem "coordinates" (Jsont.list Position.jsont)
6262+ ~enc:coordinates
6363+ |> Jsont.Object.finish
6464+end
6565+6666+module Line_string = struct
6767+ type t = { arcs : int32 list }
6868+ let make arcs = { arcs }
6969+ let arcs v = v.arcs
7070+ let jsont =
7171+ Jsont.Object.map ~kind:"LineString" make
7272+ |> Jsont.Object.mem "arcs" Jsont.(list int32) ~enc:arcs
7373+ |> Jsont.Object.finish
7474+end
7575+7676+module Multi_line_string = struct
7777+ type t = { arcs : int32 list list }
7878+ let make arcs = { arcs }
7979+ let arcs v = v.arcs
8080+ let jsont =
8181+ Jsont.Object.map ~kind:"MultiLineString" make
8282+ |> Jsont.Object.mem "arcs" Jsont.(list (list int32)) ~enc:arcs
8383+ |> Jsont.Object.finish
8484+end
8585+8686+module Polygon = struct
8787+ type t = { arcs : int32 list list }
8888+ let make arcs = { arcs }
8989+ let arcs v = v.arcs
9090+ let jsont =
9191+ Jsont.Object.map ~kind:"Polygon" make
9292+ |> Jsont.Object.mem "arcs" Jsont.(list (list int32)) ~enc:arcs
9393+ |> Jsont.Object.finish
9494+end
9595+9696+module Multi_polygon = struct
9797+ type t = { arcs : int32 list list list }
9898+ let make arcs = { arcs }
9999+ let arcs v = v.arcs
100100+ let jsont =
101101+ Jsont.Object.map ~kind:"MultiPolygon" make
102102+ |> Jsont.Object.mem "arcs" Jsont.(list (list (list int32))) ~enc:arcs
103103+ |> Jsont.Object.finish
104104+end
105105+106106+module Geometry = struct
107107+ type id = [ `Number of float | `String of string ]
108108+ let id_jsont =
109109+ let number =
110110+ let dec = Jsont.Base.dec (fun n -> `Number n) in
111111+ let enc = Jsont.Base.enc (function `Number n -> n | _ -> assert false) in
112112+ Jsont.Base.number (Jsont.Base.map ~enc ~dec ())
113113+ in
114114+ let string =
115115+ let dec = Jsont.Base.dec (fun n -> `String n) in
116116+ let enc = Jsont.Base.enc (function `String n -> n | _ -> assert false) in
117117+ Jsont.Base.string (Jsont.Base.map ~enc ~dec ())
118118+ in
119119+ let enc = function `Number _ -> number | `String _ -> string in
120120+ Jsont.any ~kind:"id" ~dec_number:number ~dec_string:string ~enc ()
121121+122122+ type t =
123123+ { type' : type';
124124+ id : id option;
125125+ properties : Jsont.json String_map.t option;
126126+ bbox : Bbox.t option;
127127+ unknown : Jsont.json }
128128+129129+ and type' =
130130+ | Point of Point.t
131131+ | Multi_point of Multi_point.t
132132+ | Line_string of Line_string.t
133133+ | Multi_line_string of Multi_line_string.t
134134+ | Polygon of Polygon.t
135135+ | Multi_polygon of Multi_polygon.t
136136+ | Geometry_collection of t list
137137+138138+ let make type' id properties bbox unknown =
139139+ { type'; id; properties; bbox; unknown }
140140+141141+ let type' g = g.type'
142142+ let id g = g.id
143143+ let properties g = g.properties
144144+ let bbox g = g.bbox
145145+ let unknown g = g.unknown
146146+147147+ let point v = Point v
148148+ let multi_point v = Multi_point v
149149+ let line_string v = Line_string v
150150+ let multi_linestr v = Multi_line_string v
151151+ let polygon v = Polygon v
152152+ let multi_polygon v = Multi_polygon v
153153+ let collection vs = Geometry_collection vs
154154+155155+ let properties_type = Jsont.Object.as_string_map ~kind:"properties" Jsont.json
156156+157157+ let rec collection_jsont = lazy begin
158158+ Jsont.Object.map ~kind:"GeometryCollection" Fun.id
159159+ |> Jsont.Object.mem "geometries" (Jsont.list (Jsont.rec' jsont)) ~enc:Fun.id
160160+ |> Jsont.Object.finish
161161+ end
162162+163163+ and jsont = lazy begin
164164+ let case_map obj dec = Jsont.Object.Case.map (Jsont.kind obj) obj ~dec in
165165+ let case_point = case_map Point.jsont point in
166166+ let case_multi_point = case_map Multi_point.jsont multi_point in
167167+ let case_line_string = case_map Line_string.jsont line_string in
168168+ let case_multi_linestr = case_map Multi_line_string.jsont multi_linestr in
169169+ let case_polygon = case_map Polygon.jsont polygon in
170170+ let case_multi_polygon = case_map Multi_polygon.jsont multi_polygon in
171171+ let case_coll = case_map (Lazy.force collection_jsont) collection in
172172+ let enc_case = function
173173+ | Point p -> Jsont.Object.Case.value case_point p
174174+ | Multi_point m -> Jsont.Object.Case.value case_multi_point m
175175+ | Line_string l -> Jsont.Object.Case.value case_line_string l
176176+ | Multi_line_string m -> Jsont.Object.Case.value case_multi_linestr m
177177+ | Polygon p -> Jsont.Object.Case.value case_polygon p
178178+ | Multi_polygon m -> Jsont.Object.Case.value case_multi_polygon m
179179+ | Geometry_collection gs -> Jsont.Object.Case.value case_coll gs
180180+ and cases = Jsont.Object.Case.[
181181+ make case_point; make case_multi_point; make case_line_string;
182182+ make case_multi_linestr; make case_polygon; make case_multi_polygon;
183183+ make case_coll ]
184184+ in
185185+ Jsont.Object.map ~kind:"Geometry" make
186186+ |> Jsont.Object.case_mem "type" Jsont.string ~enc:type' ~enc_case cases
187187+ ~tag_to_string:Fun.id ~tag_compare:String.compare
188188+ |> Jsont.Object.opt_mem "id" id_jsont ~enc:id
189189+ |> Jsont.Object.opt_mem "properties" properties_type ~enc:properties
190190+ |> Jsont.Object.opt_mem "bbox" Bbox.jsont ~enc:bbox
191191+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
192192+ |> Jsont.Object.finish
193193+ end
194194+195195+ let jsont = Lazy.force jsont
196196+ type objects = t String_map.t
197197+ let objects_jsont = Jsont.Object.as_string_map ~kind:"objects map" jsont
198198+end
199199+200200+module Topology = struct
201201+ type t =
202202+ { objects : Geometry.objects;
203203+ arcs : Arcs.t;
204204+ transform : Transform.t option;
205205+ bbox : Bbox.t option;
206206+ unknown : Jsont.json }
207207+208208+ let make objects arcs transform bbox unknown =
209209+ { objects; arcs; transform; bbox; unknown }
210210+211211+ let objects t = t.objects
212212+ let arcs t = t.arcs
213213+ let transform t = t.transform
214214+ let bbox t = t.bbox
215215+ let unknown t = t.unknown
216216+ let jsont =
217217+ let kind = "Topology" in
218218+ Jsont.Object.map ~kind (fun () -> make)
219219+ |> Jsont.Object.mem "type" (Jsont.enum [kind, ()]) ~enc:(Fun.const ())
220220+ |> Jsont.Object.mem "objects" Geometry.objects_jsont ~enc:objects
221221+ |> Jsont.Object.mem "arcs" Arcs.jsont ~enc:arcs
222222+ |> Jsont.Object.opt_mem "transform" Transform.jsont ~enc:transform
223223+ |> Jsont.Object.opt_mem "bbox" Bbox.jsont ~enc:bbox
224224+ |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown
225225+ |> Jsont.Object.finish
226226+end
227227+228228+(* Command line interface *)
229229+230230+let ( let* ) = Result.bind
231231+let strf = Printf.sprintf
232232+233233+let log_if_error ~use = function
234234+| Ok v -> v
235235+| Error e ->
236236+ let lines = String.split_on_char '\n' e in
237237+ Format.eprintf "@[%a @[<v>%a@]@]@."
238238+ Jsont.Error.puterr () (Format.pp_print_list Format.pp_print_string) lines;
239239+ use
240240+241241+let with_infile file f = (* XXX add something to bytesrw. *)
242242+ let process file ic = try Ok (f (Bytesrw.Bytes.Reader.of_in_channel ic)) with
243243+ | Sys_error e -> Error (Format.sprintf "@[<v>%s:@,%s@]" file e)
244244+ in
245245+ try match file with
246246+ | "-" -> process file In_channel.stdin
247247+ | file -> In_channel.with_open_bin file (process file)
248248+ with Sys_error e -> Error e
249249+250250+let trip ~file ~format ~locs ~dec_only =
251251+ log_if_error ~use:1 @@
252252+ with_infile file @@ fun r ->
253253+ log_if_error ~use:1 @@
254254+ let* t = Jsont_bytesrw.decode ~file ~locs Topology.jsont r in
255255+ if dec_only then Ok 0 else
256256+ let w = Bytesrw.Bytes.Writer.of_out_channel stdout in
257257+ let* () = Jsont_bytesrw.encode ~format ~eod:true Topology.jsont t w in
258258+ Ok 0
259259+260260+open Cmdliner
261261+open Cmdliner.Term.Syntax
262262+263263+let topojson =
264264+ Cmd.v (Cmd.info "topojson" ~doc:"round trip TopoJSON") @@
265265+ let+ file =
266266+ let doc = "$(docv) is the TopoJSON file. Use $(b,-) for stdin." in
267267+ Arg.(value & pos 0 string "-" & info [] ~doc ~docv:"FILE")
268268+ and+ locs =
269269+ let doc = "Preserve locations (better errors)." in
270270+ Arg.(value & flag & info ["l"; "locs"] ~doc)
271271+ and+ format =
272272+ let fmt = [ "indent", Jsont.Indent; "minify", Jsont.Minify ] in
273273+ let doc = strf "Output style. Must be %s." (Arg.doc_alts_enum fmt)in
274274+ Arg.(value & opt (enum fmt) Jsont.Minify &
275275+ info ["f"; "format"] ~doc ~docv:"FMT")
276276+ and+ dec_only =
277277+ let doc = "Decode only." in
278278+ Arg.(value & flag & info ["d"] ~doc)
279279+ in
280280+ trip ~file ~format ~locs ~dec_only
281281+282282+let main () = Cmd.eval' topojson
283283+let () = if !Sys.interactive then () else exit (main ())
+38
vendor/opam/jsont/test/trials.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2024 The jsont programmers. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+module Message = struct
77+ type t = { content : string; public : bool }
88+ let make content public = { content; public }
99+ let content msg = msg.content
1010+ let public msg = msg.public
1111+ let jsont : t Jsont.t =
1212+ Jsont.Object.map make
1313+ |> Jsont.Object.mem "content" Jsont.string ~enc:content
1414+ |> Jsont.Object.mem "public" Jsont.bool ~enc:public
1515+ |> Jsont.Object.finish
1616+end
1717+1818+type ('ret, 'f) app =
1919+| Fun : 'f -> ('ret, 'f) app
2020+| App : ('ret, 'a -> 'b) app * 'a -> ('ret, 'b) app
2121+2222+let ret : 'f -> ('ret, 'f) app = fun f -> Fun f
2323+let app : ('ret, 'a -> 'b) app -> 'a -> ('ret, 'b) app = fun f a -> App (f, a)
2424+2525+let g ~i ~s = string_of_int i ^ s
2626+2727+let t0 : (string, string) app =
2828+ app (app (ret (fun i s -> g ~i ~s)) 2) "bla"
2929+3030+(* That works but it's not the tructure that we want. *)
3131+3232+let ( let+ ) : 'a -> ('a -> 'b) -> ('ret, 'b) app = fun v f -> App (Fun f, v)
3333+let ( and+ ) : 'a -> 'b -> 'a * 'b = fun x y -> (x, y)
3434+3535+let t1 : (string, string) app =
3636+ let+ i = 2
3737+ and+ s = "bla" in
3838+ g ~i ~s