Skip to content

Commit 5eafe34

Browse files
authored
Merge pull request #63 from gasche/proper-json-errors
proper JSON errors
2 parents e56e89a + 6baf029 commit 5eafe34

File tree

6 files changed

+77
-12
lines changed

6 files changed

+77
-12
lines changed

bin/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
(name mustache_cli)
33
(public_name mustache)
44
(modules mustache_cli)
5-
(libraries mustache ezjsonm cmdliner))
5+
(libraries mustache jsonm cmdliner))
66

77
(rule
88
(with-stdout-to mustache.1

bin/mustache_cli.ml

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,72 @@ let load_template template_filename =
3333
Mustache.pp_template_parse_error err;
3434
exit 3
3535

36+
module Json = struct
37+
type error =
38+
| Jsonm of Jsonm.error
39+
| Incomplete_input
40+
| Parse_error of string
41+
42+
type jsonm_loc = (int * int) * (int * int)
43+
44+
exception Error of jsonm_loc * error
45+
46+
let json_of_decoder (d : Jsonm.decoder) =
47+
(* inspired from the Jsonm documentation example *)
48+
let raise_err err = raise (Error (Jsonm.decoded_range d, err)) in
49+
let dec d = match Jsonm.decode d with
50+
| `Lexeme l -> l
51+
| `Error e -> raise_err (Jsonm e)
52+
| `End | `Await -> raise_err Incomplete_input
53+
in
54+
let wrap k = (k : Mustache.Json.value -> 'r :> Mustache.Json.t -> 'r) in
55+
let rec value v (k : Mustache.Json.value -> _) d = match v with
56+
| `Os -> obj [] (wrap k) d | `As -> arr [] (wrap k) d
57+
| `Null | `Bool _ | `String _ | `Float _ as v -> k v d
58+
| _ -> raise_err (Parse_error "value fields expected")
59+
and arr vs (k : Mustache.Json.t -> _) d = match dec d with
60+
| `Ae -> k (`A (List.rev vs)) d
61+
| v -> value v (fun v -> arr (v :: vs) k) d
62+
and obj ms (k : Mustache.Json.t -> _) d = match dec d with
63+
| `Oe -> k (`O (List.rev ms)) d
64+
| `Name n -> value (dec d) (fun v -> obj ((n, v) :: ms) k) d
65+
| _ -> raise_err (Parse_error "object fields expected")
66+
in
67+
let t v (k : Mustache.Json.t -> _) d = match v with
68+
| `Os -> obj [] k d | `As -> arr [] k d
69+
| _ -> raise_err (Parse_error "Json.t expected")
70+
in
71+
t (dec d) (fun v _ -> v) d
72+
73+
let pp_error ppf (fname, jsonm_loc, error) =
74+
let ((start_line, start_col), (end_line, end_col)) = jsonm_loc in
75+
let lexpos line col : Lexing.position = {
76+
pos_fname = fname;
77+
pos_lnum = line;
78+
pos_bol = 0;
79+
pos_cnum = col;
80+
} in
81+
let loc : Mustache.loc = {
82+
loc_start = lexpos start_line start_col;
83+
loc_end = lexpos end_line end_col;
84+
} in
85+
Format.fprintf ppf "%a:@ %t"
86+
Mustache.pp_loc loc
87+
(fun ppf -> match error with
88+
| Jsonm e -> Jsonm.pp_error ppf e
89+
| Incomplete_input -> Format.fprintf ppf "Incomplete input."
90+
| Parse_error s -> Format.fprintf ppf "Parse error: %s." s
91+
)
92+
end
93+
3694
let load_json json_filename =
37-
Ezjsonm.from_string (load_file json_filename)
95+
let input = load_file json_filename in
96+
let decoder = Jsonm.decoder (`String input) in
97+
try Json.json_of_decoder decoder with
98+
| Json.Error (jsonm_loc, error) ->
99+
Format.eprintf "%a@."
100+
Json.pp_error (json_filename, jsonm_loc, error);
101+
exit 4
38102

39103
let run search_path json_filename template_filename =
40104
let env = load_json json_filename in

bin/test/errors/json-errors.t

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@
44

55
Empty json file:
66
$ mustache empty.json foo.mustache
7-
mustache: internal error, uncaught exception:
8-
Ezjsonm.Parse_error(870828711, "JSON.of_buffer expected JSON text (JSON value)")
9-
10-
[125]
7+
File "empty.json", line 1, character 0: expected JSON text (JSON value)
8+
[4]
119

1210
Invalid json file:
1311
$ mustache invalid.json foo.mustache
14-
mustache: internal error, uncaught exception:
15-
Ezjsonm.Parse_error(870828711, "JSON.of_buffer expected value separator or object end (',' or '}')")
16-
17-
[125]
12+
File "invalid.json", line 1, characters 15-29:
13+
expected value separator or object end (',' or '}')
14+
[4]

dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ Read and write mustache templates, and render them by providing a json object.
2424
Contains the `mustache` command line utility for driving logic-less templates.
2525
")
2626
(depends
27-
ezjsonm
27+
(jsonm (>= 1.0.1))
2828
(ounit :with-test)
29+
(ezjsonm :with-test)
2930
(menhir (>= 20180703))
3031
(cmdliner (>= 1.0.4))
3132
(ocaml (>= 4.06))))

lib/mustache.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type loc =
4040
{ loc_start: Lexing.position;
4141
loc_end: Lexing.position }
4242

43+
val pp_loc : Format.formatter -> loc -> unit
44+
4345
(** Read template files; those function may raise [Parse_error]. *)
4446
type template_parse_error
4547
exception Parse_error of template_parse_error

mustache.opam

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ homepage: "https://github.com/rgrinberg/ocaml-mustache"
1717
bug-reports: "https://github.com/rgrinberg/ocaml-mustache/issues"
1818
depends: [
1919
"dune" {>= "2.7"}
20-
"ezjsonm"
20+
"jsonm" {>= "1.0.1"}
2121
"ounit" {with-test}
22+
"ezjsonm" {with-test}
2223
"menhir" {>= "20180703"}
2324
"cmdliner" {>= "1.0.4"}
2425
"ocaml" {>= "4.06"}

0 commit comments

Comments
 (0)