Skip to content

Commit 12c1785

Browse files
committed
refactor: Error.t
1 parent 0b41316 commit 12c1785

File tree

5 files changed

+93
-102
lines changed

5 files changed

+93
-102
lines changed

src/decode.ml

Lines changed: 29 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@
22

33
open Decoders_util
44

5-
type 'value exposed_error =
6-
| Decoder_error of string * 'value option
7-
| Decoder_errors of 'value exposed_error list
8-
| Decoder_tag of string * 'value exposed_error
9-
105
type ('good, 'bad) result = ('good, 'bad) My_result.t =
116
| Ok of 'good
127
| Error of 'bad
138

14-
type ('value, 'a) exposed_decoder = ('value, 'a, 'value exposed_error) Decoder.t
15-
169
(** Signature of things that can be decoded. *)
1710
module type Decodeable = sig
1811
type value
@@ -44,7 +37,7 @@ end
4437
module type S = sig
4538
type value
4639

47-
type error = value exposed_error
40+
type error = value Error.t
4841

4942
val pp_error : Format.formatter -> error -> unit
5043

@@ -165,41 +158,18 @@ end
165158
module Make (Decodeable : Decodeable) :
166159
S
167160
with type value = Decodeable.value
168-
and type 'a decoder = (Decodeable.value, 'a) exposed_decoder = struct
161+
and type 'a decoder =
162+
(Decodeable.value, 'a, Decodeable.value Error.t) Decoder.t = struct
169163
type value = Decodeable.value
170164

171165
let pp = Decodeable.pp
172166

173-
type error = value exposed_error
174-
175-
let rec pp_error fmt = function
176-
| Decoder_error (msg, Some t) ->
177-
Format.fprintf fmt "@[%s, but got@ @[%a@]@]" msg pp t
178-
| Decoder_error (msg, None) ->
179-
Format.fprintf fmt "@[%s@]" msg
180-
| Decoder_errors errors ->
181-
let errors_trunc = My_list.take 5 errors in
182-
let not_shown = List.length errors - 5 in
183-
Format.fprintf
184-
fmt
185-
"@[%a@ %s@]"
186-
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_error)
187-
errors_trunc
188-
( if not_shown > 0
189-
then Printf.sprintf "(...%d errors not shown...)" not_shown
190-
else "" )
191-
| Decoder_tag (msg, error) ->
192-
Format.fprintf fmt "@[<2>%s:@ @[%a@]@]" msg pp_error error
167+
type error = value Error.t
193168

169+
let pp_error = Error.pp ~pp_i:pp
194170

195171
let string_of_error e : string = Format.asprintf "@[<2>%a@?@]" pp_error e
196172

197-
let tag_error (msg : string) (error : error) : error = Decoder_tag (msg, error)
198-
199-
let tag_errors (msg : string) (errors : error list) : error =
200-
Decoder_tag (msg, Decoder_errors errors)
201-
202-
203173
let combine_errors (results : ('a, error) result list) :
204174
('a list, error list) result =
205175
let rec aux combined = function
@@ -230,31 +200,25 @@ module Make (Decodeable : Decodeable) :
230200
fun string ->
231201
Decodeable.of_string string
232202
|> My_result.map_err (fun msg ->
233-
Decoder_tag ("Json parse error", Decoder_error (msg, None)))
203+
Error.tag "Json parse error" (Error.make msg) )
234204

235205

236206
let of_file : string -> (value, error) result =
237207
fun file ->
238208
Decodeable.of_file file
239209
|> My_result.map_err (fun msg ->
240-
Decoder_tag
241-
(Printf.sprintf "While reading %s" file, Decoder_error (msg, None)))
210+
Error.tag (Printf.sprintf "While reading %s" file) (Error.make msg) )
242211

243212

244-
type 'a decoder = (value, 'a) exposed_decoder
213+
type 'a decoder = (value, 'a, value Error.t) Decoder.t
245214

246215
let succeed x = Decoder.pure x
247216

248-
let fail msg input = Error (Decoder_error (msg, Some input))
217+
let fail msg input = Error (Error.make msg ~context:input)
249218

250219
let fail_with error = Decoder.fail error
251220

252-
let from_result = function
253-
| Ok ok ->
254-
succeed ok
255-
| Error error ->
256-
fail_with error
257-
221+
let from_result = Decoder.of_result
258222

259223
let value = Decoder.value
260224

@@ -296,7 +260,7 @@ module Make (Decodeable : Decodeable) :
296260
| None ->
297261
decoder input
298262
|> My_result.map My_opt.return
299-
|> My_result.map_err (tag_error "Expected null or")
263+
|> My_result.map_err (Error.tag "Expected null or")
300264

301265

302266
let one_of (decoders : (string * 'a decoder) list) : 'a decoder =
@@ -305,12 +269,12 @@ module Make (Decodeable : Decodeable) :
305269
|> My_list.map (fun (name, d) ->
306270
d
307271
|> Decoder.map_err (fun e ->
308-
tag_errors (Printf.sprintf "%S decoder" name) [ e ] ) )
272+
Error.tag_group (Printf.sprintf "%S decoder" name) [ e ] ) )
309273
in
310274
Decoder.one_of
311275
decoders
312276
~combine_errors:
313-
(tag_errors "I tried the following decoders but they all failed")
277+
(Error.tag_group "I tried the following decoders but they all failed")
314278

315279

316280
let primitive_decoder (get_value : value -> 'a option) (message : string) :
@@ -346,9 +310,9 @@ module Make (Decodeable : Decodeable) :
346310
values
347311
|> My_list.mapi (fun i x ->
348312
decoder x
349-
|> My_result.map_err (tag_error (Printf.sprintf "element %i" i)) )
313+
|> My_result.map_err (Error.tag (Printf.sprintf "element %i" i)) )
350314
|> combine_errors
351-
|> My_result.map_err (tag_errors "while decoding a list")
315+
|> My_result.map_err (Error.tag_group "while decoding a list")
352316

353317

354318
let list_filter : 'a option decoder -> 'a list decoder =
@@ -359,7 +323,7 @@ module Make (Decodeable : Decodeable) :
359323
| v :: vs ->
360324
My_result.Infix.(
361325
decoder v
362-
|> My_result.map_err (tag_error (Printf.sprintf "element %i" i))
326+
|> My_result.map_err (Error.tag (Printf.sprintf "element %i" i))
363327
>>= (function
364328
| Some x ->
365329
go (i + 1) vs >>= fun xs -> My_result.return (x :: xs)
@@ -371,7 +335,7 @@ module Make (Decodeable : Decodeable) :
371335
| None ->
372336
(fail "Expected a list") t
373337
| Some values ->
374-
go 0 values |> My_result.map_err (tag_error "while decoding a list")
338+
go 0 values |> My_result.map_err (Error.tag "while decoding a list")
375339

376340

377341
let list_fold_left : ('a -> 'a decoder) -> 'a -> 'a decoder =
@@ -388,11 +352,11 @@ module Make (Decodeable : Decodeable) :
388352
>>= fun acc ->
389353
(acc |> decoder_func) el
390354
|> My_result.map_err
391-
(tag_error (Printf.sprintf "element %i" i)) )
355+
(Error.tag (Printf.sprintf "element %i" i)) )
392356
, i + 1 ) )
393357
(Ok init, 0))
394358
|> fst
395-
|> My_result.map_err (tag_error "while decoding a list")
359+
|> My_result.map_err (Error.tag "while decoding a list")
396360

397361

398362
let array : 'a decoder -> 'a array decoder =
@@ -401,8 +365,8 @@ module Make (Decodeable : Decodeable) :
401365
match res with
402366
| Ok x ->
403367
Ok (Array.of_list x)
404-
| Error (Decoder_tag ("while decoding a list", e)) ->
405-
Error (Decoder_tag ("while decoding an array", e))
368+
| Error (Tag ("while decoding a list", e)) ->
369+
Error (Tag ("while decoding an array", e))
406370
| Error e ->
407371
Error e
408372

@@ -422,7 +386,7 @@ module Make (Decodeable : Decodeable) :
422386
match value with
423387
| Some value ->
424388
value_decoder value
425-
|> My_result.map_err (tag_error (Printf.sprintf "in field %S" key))
389+
|> My_result.map_err (Error.tag (Printf.sprintf "in field %S" key))
426390
| None ->
427391
(fail (Printf.sprintf "Expected an object with an attribute %S" key)) t
428392

@@ -443,7 +407,7 @@ module Make (Decodeable : Decodeable) :
443407
| Some value ->
444408
value_decoder value
445409
|> My_result.map (fun v -> Some v)
446-
|> My_result.map_err (tag_error (Printf.sprintf "in field %S" key))
410+
|> My_result.map_err (Error.tag (Printf.sprintf "in field %S" key))
447411
| None ->
448412
Ok None
449413

@@ -455,7 +419,7 @@ module Make (Decodeable : Decodeable) :
455419
( match Decodeable.get_string key with
456420
| Some key ->
457421
(value_decoder key) value
458-
|> My_result.map_err (tag_error (Printf.sprintf "in field %S" key))
422+
|> My_result.map_err (Error.tag (Printf.sprintf "in field %S" key))
459423
| None ->
460424
(fail "Expected an object with a string key") t )
461425
| _ ->
@@ -490,10 +454,10 @@ module Make (Decodeable : Decodeable) :
490454
| Some (x :: rest) ->
491455
My_result.Infix.(
492456
head x
493-
|> My_result.map_err (tag_error "while consuming a list element")
457+
|> My_result.map_err (Error.tag "while consuming a list element")
494458
>>= fun x ->
495459
(tail x) (Decodeable.to_list rest)
496-
|> My_result.map_err (tag_error "after consuming a list element"))
460+
|> My_result.map_err (Error.tag "after consuming a list element"))
497461
| Some [] ->
498462
(fail "Expected a non-empty list") value
499463
| None ->
@@ -519,7 +483,7 @@ module Make (Decodeable : Decodeable) :
519483
|> List.map (fun (key, _) -> key_decoder key)
520484
|> combine_errors
521485
|> My_result.map_err
522-
(tag_errors "Failed while decoding the keys of an object")
486+
(Error.tag_group "Failed while decoding the keys of an object")
523487
| None ->
524488
(fail "Expected an object") value
525489

@@ -539,7 +503,7 @@ module Make (Decodeable : Decodeable) :
539503
value_decoder value_val >|= fun value -> (key, value))
540504
|> combine_errors
541505
|> My_result.map_err
542-
(tag_errors "Failed while decoding key-value pairs")
506+
(Error.tag_group "Failed while decoding key-value pairs")
543507
| None ->
544508
(fail "Expected an object") value
545509

@@ -559,7 +523,7 @@ module Make (Decodeable : Decodeable) :
559523
>>= fun key -> (value_decoder key) value_val)
560524
|> combine_errors
561525
|> My_result.map_err
562-
(tag_errors "Failed while decoding key-value pairs")
526+
(Error.tag_group "Failed while decoding key-value pairs")
563527
| None ->
564528
(fail "Expected an object") value
565529

src/decode.mli

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
type 'value exposed_error =
2-
| Decoder_error of string * 'value option
3-
| Decoder_errors of 'value exposed_error list
4-
| Decoder_tag of string * 'value exposed_error
5-
61
type ('good, 'bad) result = ('good, 'bad) Decoders_util.My_result.t =
72
| Ok of 'good
83
| Error of 'bad
94

10-
type ('value, 'a) exposed_decoder = ('value, 'a, 'value exposed_error) Decoder.t
11-
125
(** User-facing Decoder interface. *)
136
module type S = sig
147
(** The type of values to be decoded (e.g. JSON or Yaml). *)
158
type value
169

17-
type error = value exposed_error
10+
type error = value Error.t
1811

1912
val pp_error : Format.formatter -> error -> unit
2013

@@ -323,4 +316,4 @@ end
323316
module Make (M : Decodeable) :
324317
S
325318
with type value = M.value
326-
and type 'a decoder = (M.value, 'a) exposed_decoder
319+
and type 'a decoder = (M.value, 'a, M.value Error.t) Decoder.t

src/decoders.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module Decoder = Decoder
2+
module Error = Error
23
module Decode = Decode
34
module Encode = Encode
45
module Decoders_util = Decoders_util

src/error.ml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
type 'i t =
2+
| E of
3+
{ msg : string
4+
; context : 'i option
5+
}
6+
| Tag of string * 'i t
7+
| Group of 'i t list
8+
9+
let make ?context msg = E { msg; context }
10+
11+
let tag msg e = Tag (msg, e)
12+
13+
let group es = Group es
14+
15+
let tag_group msg es = tag msg (group es)
16+
17+
let rec pp ~pp_i fmt =
18+
let open Format in
19+
function
20+
| E { msg; context = None } ->
21+
fprintf fmt "@[%s@]" msg
22+
| E { msg; context = Some context } ->
23+
fprintf fmt "@[%s, but got@ @[%a@]@]" msg pp_i context
24+
| Tag (msg, e) ->
25+
fprintf fmt "@[<2>%s:@ %a@]" msg (pp ~pp_i) e
26+
| Group es ->
27+
let max_errors = 5 in
28+
let es_trunc = Decoders_util.My_list.take max_errors es in
29+
let not_shown = List.length es - max_errors in
30+
fprintf
31+
fmt
32+
"@[%a %s@]"
33+
(Format.pp_print_list ~pp_sep:Format.pp_print_space (pp ~pp_i))
34+
es_trunc
35+
( if not_shown > 0
36+
then Printf.sprintf "(...%d errors not shown...)" not_shown
37+
else "" )

test-yojson/main.ml

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -139,34 +139,30 @@ let yojson_basic_suite =
139139
|}
140140
in
141141
let expected_error =
142-
let open Decoders.Decode in
143-
Decoder_tag
144-
( {|in field "records"|}
145-
, Decoder_tag
146-
( "while decoding a list"
147-
, Decoder_errors
148-
[ Decoder_tag
149-
( "element 0"
150-
, Decoder_error
151-
( {|Expected an object with an attribute "x"|}
152-
, Some (`Bool true) ) )
153-
; Decoder_tag
154-
( "element 1"
155-
, Decoder_tag
156-
( {|in field "x"|}
157-
, Decoder_tag
158-
( "while decoding a list"
159-
, Decoder_errors
160-
[ Decoder_tag
161-
( "element 0"
162-
, Decoder_error
163-
("Expected a string", Some (`Int 1)) )
164-
; Decoder_tag
165-
( "element 2"
166-
, Decoder_error
167-
("Expected a string", Some (`Int 3)) )
168-
] ) ) )
169-
] ) )
142+
let open Decoders in
143+
Error.tag
144+
{|in field "records"|}
145+
(Error.tag_group
146+
"while decoding a list"
147+
[ Error.tag
148+
"element 0"
149+
(Error.make
150+
{|Expected an object with an attribute "x"|}
151+
~context:(`Bool true) )
152+
; Error.tag
153+
"element 1"
154+
(Error.tag
155+
{|in field "x"|}
156+
(Error.tag_group
157+
"while decoding a list"
158+
[ Error.tag
159+
"element 0"
160+
(Error.make "Expected a string" ~context:(`Int 1))
161+
; Error.tag
162+
"element 2"
163+
(Error.make "Expected a string" ~context:(`Int 3))
164+
] ) )
165+
] )
170166
in
171167
match decode_string decoder input with
172168
| Ok _ ->

0 commit comments

Comments
 (0)