Skip to content

Commit e773baa

Browse files
author
Josef Erben
committed
Return triple in decode() and add decode_and_validate()
1 parent 593c2d9 commit e773baa

File tree

5 files changed

+222
-105
lines changed

5 files changed

+222
-105
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.3.0 - 2021-03-26
2+
### Changed
3+
- `decode` returns a triple containing `(field_name, input, error_msg)` instead of a concatenated string. This makes it easier to extract information.
4+
5+
### Added
6+
- `decode_and_validate` combines `decode` and `validate` where the returned value is either the decoded value or a list of errors. When using `decode_and_validate`, one can forget about the difference between `decode` and `validate` and simply forward the list of errors. This covers a common use case.
7+
18
## 0.2.1 - 2021-03-16
29
### Changed
310
- Replace `ppx_deriving` with `sexplib`

src/conformist.ml

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,9 @@ let validate schema input =
202202

203203
let rec decode
204204
: type meta ctor ty.
205-
(meta, ctor, ty) t -> (string * string list) list -> (ty, string) Result.t
205+
(meta, ctor, ty) t
206+
-> (string * string list) list
207+
-> (ty, string * string option * string) Result.t
206208
=
207209
fun { fields; ctor } fields_assoc ->
208210
let open! Field in
@@ -215,59 +217,47 @@ let rec decode
215217
| Ok value ->
216218
(match ctor value with
217219
| ctor -> decode { fields; ctor } fields_assoc
218-
| exception _ ->
219-
Error
220-
(Printf.sprintf
221-
"Failed to decode value '%s' of field '%s'"
222-
value_string
223-
field.name))
224-
| Error msg ->
225-
Error
226-
(Printf.sprintf
227-
"Failed to decode value '%s' of field '%s': %s"
228-
field.name
229-
value_string
230-
msg))
220+
| exception exn ->
221+
let msg = Printexc.to_string exn in
222+
Error (field.name, Some value_string, msg))
223+
| Error msg -> Error (field.name, Some value_string, msg))
231224
| [] ->
232225
(match field.default with
233226
| Some value ->
234227
(match ctor value with
235228
| ctor -> decode { fields; ctor } fields_assoc
236-
| exception _ ->
237-
Error (Printf.sprintf "Failed to construct field '%s'" field.name))
229+
| exception exn ->
230+
let msg = Printexc.to_string exn in
231+
Error (field.name, Some "", msg))
238232
| None ->
239233
(match field.decoder "" with
240234
| Ok value ->
241235
(match ctor value with
242236
| ctor -> decode { fields; ctor } fields_assoc
243-
| exception _ ->
244-
Error
245-
(Printf.sprintf
246-
"Failed to decode value '%s' of field '%s'"
247-
""
248-
field.name))
249-
| Error msg ->
250-
Error
251-
(Printf.sprintf
252-
"Failed to decode value '%s' of field '%s': %s"
253-
field.name
254-
""
255-
msg)))
256-
| _ ->
257-
Error
258-
(Printf.sprintf
259-
"Failed to decode field '%s': Multiple values provided"
260-
field.name)
237+
| exception exn ->
238+
let msg = Printexc.to_string exn in
239+
Error (field.name, Some "", msg))
240+
| Error msg -> Error (field.name, Some "", msg)))
241+
| _ -> Error (field.name, None, "Multiple values provided")
261242
| exception Not_found ->
262243
(match field.default with
263244
| Some value ->
264245
(match ctor value with
265246
| ctor -> decode { fields; ctor } fields_assoc
266-
| exception _ ->
267-
Error (Printf.sprintf "Failed to construct field '%s'" field.name))
268-
| None ->
269-
Error
270-
(Printf.sprintf
271-
"Failed to decode field '%s': No value provided"
272-
field.name)))
247+
| exception exn ->
248+
let msg = Printexc.to_string exn in
249+
Error (field.name, None, msg))
250+
| None -> Error (field.name, None, "No value provided")))
251+
;;
252+
253+
let decode_and_validate schema input =
254+
let validation_errors = validate schema input in
255+
match decode schema input, validation_errors with
256+
| Ok value, [] -> Ok value
257+
| Ok _, validation_errors -> Error validation_errors
258+
| Error (field_name, _, msg), validation_errors ->
259+
validation_errors
260+
|> List.filter (fun (name, _) -> not (String.equal name field_name))
261+
|> List.cons (field_name, msg)
262+
|> Result.error
273263
;;

src/conformist.mli

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,41 @@ type validation_error = (string * string) list
309309
This is typically some user input. *)
310310
type input = (string * string list) list
311311

312-
(** [decode schema input] tries to create a value of the static type ['ty]. Note
313-
that a successfully decoded value means that the strings contain the
314-
expected types, but no validation logic was executed. *)
315-
val decode : ('meta, 'ctor, 'ty) t -> input -> ('ty, string) result
316-
317-
(** [validate schema input] runs the field validators on decoded data. Note that
318-
a field that fails to decode will also fail validation, but a decoded field
319-
might still fail validation. *)
312+
(** [decode schema input] returns the decoded value of type ['ty] by decoding
313+
the [input] using the [schema].
314+
315+
The returned error value is a triple [(field_name, input_value, error_msg)].
316+
[field_name] is the field name of the input that failed to decode,
317+
[input_value] is the input value if one was provided and [error_msg] is the
318+
error message.
319+
320+
No validation logic is executed in this step. *)
321+
val decode
322+
: ('meta, 'ctor, 'ty) t
323+
-> input
324+
-> ('ty, string * string option * string) result
325+
326+
(** [validate schema input] returns a list of validation errors by running the
327+
validators defined in [schema] on the [input] data. An empty list implies
328+
that there are no validation errors and that the input is valid according to
329+
the schema.
330+
331+
Note that [input] that has no validation errors might still fail to decode,
332+
depending on the validation functions specified in [schema]. *)
320333
val validate : ('meta, 'ctor, 'ty) t -> input -> validation_error
334+
335+
(** [decode_and_validate schema input] returns the decoded and validated value
336+
of type ['ty] by decoding the [input] using the [schema] and running its
337+
validators.
338+
339+
The returned error is a {!type:validation_error}. If a field fails to
340+
decode, the error of that field is the decode error. If a field decodes but
341+
fails to validate, then the error is the validation error.
342+
343+
Use [decode_and_validate] to combine the functions [decode] and [validate]
344+
and to either end up with the decoded value or all errors that happened
345+
during the decoding and validation steps. *)
346+
val decode_and_validate
347+
: ('meta, 'ctor, 'ty) t
348+
-> input
349+
-> ('ty, validation_error) Result.t

0 commit comments

Comments
 (0)