Source locations and structured errors for text codecs (extracted from jsont)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

wal, block, sse, zephyr: remove [mutable] from never-reassigned fields

Warning 69 (unused-field, mutable-never-assigned). Four independent
record fields were flagged as mutable but the code only mutates their
referents in place, never rebinds the record slot itself:

- ocaml-wal/lib/wal.ml: [t.file] (the Eio file resource; methods call
Eio.File.pwrite_all etc., the slot is set once at open time).
- ocaml-block/lib/block.ml: [Memory.state.data] (the backing bytes,
written via Bytes.blit_string; [Bytes.t] is already mutable).
- ocaml-sse/lib/sse.ml: [Parser.t.data_buf] (a Buffer.t, written via
Buffer.add_*; the slot never changes).
- ocaml-zephyr/lib/zephyr.ml: drop [mode : Read | Write] entirely —
set at open-time, read nowhere. The open_read / open_write
constructors already distinguish the two call shapes, so mode
tracking was redundant.

+52 -38
+49 -38
README.md
··· 1 1 # loc 2 2 3 - Source-location and structured-error infrastructure for text-based 4 - codecs, extracted from Daniel Bünzli's 5 - [jsont](https://erratique.ch/software/jsont). Packaging the shared 6 - base as a standalone library lets non-JSON text codecs (`xmlt`, 7 - `csv`, `sexpt`, ...) reuse it without pulling in `jsont`. 3 + Source locations and structured errors for text-based codecs in OCaml, 4 + extracted from Daniel Bünzli's 5 + [jsont](https://erratique.ch/software/jsont). Packaging the shared base 6 + as a standalone library lets non-JSON text codecs (`xml`, `csv`, `sexp`, 7 + `toml`, ...) report error positions uniformly without pulling in 8 + `jsont`. 8 9 9 10 A direct copy of `jsont_base.ml` and the `Error` module of `jsont.ml`, 10 - with JSON-specific pieces (the `Number` module, the JSON `Sort.t` 11 - enum, JSON-shaped predefined errors) removed; everything else is 12 - verbatim. 11 + with JSON-specific pieces (the `Number` module, the JSON `Sort.t` enum, 12 + JSON-shaped predefined errors) removed; everything else is verbatim. 13 13 14 14 Provides: 15 15 16 - - `Loc` -- byte/line/column ranges for source text 17 - - `Meta` -- node metadata (location + surrounding whitespace) 18 - - `Path` -- structural paths (`Mem of string node | Nth of int node`) 19 - - `Error` -- contextualised errors with a kind, source location, and 20 - structural path (`Error.t = Context.t * Meta.t * kind`) 21 - - `exception Error of Error.t` -- single shared exception for 16 + - `Loc` — byte and line ranges for UTF-8 source text 17 + - `Loc.Meta` — node metadata: source location and surrounding whitespace 18 + - `Loc.Path` — structural paths (`Mem of string node | Nth of int node`) 19 + - `Loc.Error` — structured errors: extensible kind, source location, 20 + contextual path; formatted per the OCaml compiler / GNU error 21 + conventions 22 + - `exception Loc.Error of Loc.Error.t` — single shared exception for 22 23 cross-package error propagation 23 24 24 - ## Installation 25 + ## Install 25 26 26 27 ``` 27 28 opam install loc 28 29 ``` 30 + 31 + Requires OCaml >= 4.14 and `fmt`. 29 32 30 33 ## Usage 31 34 35 + Raise a structured error at a specific source location: 36 + 32 37 ```ocaml 33 - (* A library reports an error at a specific source location. *) 34 38 let loc = 35 39 Loc.make ~file:"config.toml" 36 40 ~first_byte:42 ~last_byte:47 ··· 39 43 40 44 let meta = Loc.Meta.make loc 41 45 42 - (* Raise a structured error; library code can catch and inspect it, 43 - or format it for display. *) 44 46 let () = 45 - try 46 - Loc.Error.msgf meta "expected %s, got %s" "integer" "\"nope\"" 47 - with Loc.Error e -> 48 - print_endline (Loc.Error.to_string e) 49 - (* expected integer, got "nope" 50 - File "config.toml", line 3, characters 42-48: *) 47 + try Loc.Error.failf meta "expected %s, got %s" "integer" "\"nope\"" 48 + with Loc.Error e -> print_endline (Loc.Error.to_string e) 49 + ``` 50 + 51 + Wrap a raised error with structural context as it bubbles up through 52 + nested object members: 51 53 52 - (* Wrap a raised error with structural context as it bubbles up. *) 54 + ```ocaml 53 55 let () = 54 - let root = ("table [server]", Loc.Meta.none) in 56 + let table = ("table [server]", Loc.Meta.none) in 55 57 try 56 - try 57 - Loc.Error.msg Loc.Meta.none "not a number" 58 + try Loc.Error.fail Loc.Meta.none "not a number" 58 59 with Loc.Error e -> 59 - Loc.Error.push_object root ("port", Loc.Meta.none) e 60 - with Loc.Error e -> 61 - print_endline (Loc.Error.to_string e) 60 + Loc.Error.push_object table ("port", Loc.Meta.none) e 61 + with Loc.Error e -> print_endline (Loc.Error.to_string e) 62 + ``` 63 + 64 + Extend the error kind for a domain-specific codec and register a 65 + printer: 66 + 67 + ```ocaml 68 + type Loc.Error.kind += Unknown_key of string 69 + 70 + let () = 71 + Loc.Error.register_kind_printer (function 72 + | Unknown_key k -> Some (fun ppf -> Format.fprintf ppf "unknown key %S" k) 73 + | _ -> None) 62 74 ``` 63 75 64 76 ## API 65 77 66 - Full signatures in [`loc.mli`](lib/loc.mli). 78 + See [`lib/loc.mli`](lib/loc.mli) for the full signature. 67 79 68 80 ## Upstream 69 81 70 - - [jsont](https://erratique.ch/software/jsont) -- Daniel Bünzli's 71 - JSON codec library, of which this package is an extracted subset 82 + - [jsont](https://erratique.ch/software/jsont) — Daniel Bünzli's JSON 83 + codec library, of which this package is an extracted subset. 72 84 - [jsont_base.ml](https://github.com/dbuenzli/jsont/blob/main/src/jsont_base.ml) 73 - -- the source `Loc`, `Meta`, `Path`, and the `Fmt` helper are 74 - copied from 85 + — source for `Loc`, `Meta`, `Path`, and the `Fmt` helper. 75 86 - [jsont.ml](https://github.com/dbuenzli/jsont/blob/main/src/jsont.ml) 76 - -- the source the `Error` module is copied from (minus JSON-specific 77 - predefined errors) 87 + — source for the `Error` module (minus JSON-specific predefined 88 + errors). 78 89 79 90 ## License 80 91
+3
dune
··· 1 + (env 2 + (dev 3 + (flags :standard %{dune-warnings})))