Skip to content

Commit bd638ca

Browse files
committed
Keep the error type opaque. #13
1 parent b16a7e8 commit bd638ca

7 files changed

Lines changed: 90 additions & 28 deletions

File tree

drivers/generic/ppx_protocol_driver.ml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,28 @@ let mangle str =
7070

7171
module Make(Driver: Driver)(P: Parameters) = struct
7272
type t = Driver.t
73+
type error = string * t option
74+
exception Protocol_error of error
75+
76+
let error_to_string_hum: error -> string = function
77+
| (s, Some t) -> Printf.sprintf "%s. Got: %s" s (Driver.to_string_hum t)
78+
| (s, None) -> s
7379

74-
exception Protocol_error of string * t option
7580
(* Register exception printer *)
7681
let () = Printexc.register_printer (function
77-
| Protocol_error (s, Some t) -> Some (Printf.sprintf "%s, %s" s (Driver.to_string_hum t))
78-
| Protocol_error (s, None) -> Some (Printf.sprintf "%s" s)
82+
| Protocol_error err -> Some (error_to_string_hum err)
7983
| _ -> None)
8084

8185
let to_string_hum = Driver.to_string_hum
8286

8387
let raise_errorf t fmt =
8488
Caml.Printf.kprintf (fun s -> raise (Protocol_error (s, t))) fmt
8589

90+
let try_with: (t -> 'a) -> t -> ('a, error) Runtime.result = fun f t ->
91+
match f t with
92+
| v -> Ok v
93+
| exception (Protocol_error e) -> Error e
94+
8695
let wrap t f x = match f x with
8796
| v -> v
8897
| exception Helper.Protocol_error s -> raise (Protocol_error (s, Some t))

drivers/json/test/bench.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module type Test = sig
1818
val t: unit -> t
1919
val to_json: t -> Json.t
2020
val to_yojson: t -> Yojson.Safe.json
21-
val of_json: Json.t -> t Protocol_conv.Runtime.or_error
21+
val of_json: Json.t -> (t, Json.t) Protocol_conv.Runtime.result
2222
val of_yojson: Yojson.Safe.json -> t Ppx_deriving_yojson_runtime.error_or
2323
end
2424

drivers/json/test/test_expect.ml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,13 @@ module Test = struct
169169
[@@deriving protocol ~driver:(module Json)]
170170

171171
let%expect_test _ =
172-
let (a, b) = of_json_exn (`List [ `Int 5; `String "string"]) in
173-
Printf.printf "`Fst: %d\n%!" a;
172+
let (a, b) = of_json_exn (`List [ `Int 5; `String "ipsum"]) in
173+
Printf.printf "First: %d\n%!" a;
174174
begin
175175
try
176-
Printf.printf "`Snd: %d\n" (Lazy.force b)
176+
Printf.printf "Lazy: %d\n" (Lazy.force b)
177177
with
178-
| Json.Protocol_error (msg, Some t) -> Printf.eprintf "`Snd: %s. Got: %s" msg (Json.to_string_hum t);
179-
| Json.Protocol_error (msg, None) -> Printf.eprintf "`Snd: %s. Got: None" msg
178+
| Json.Protocol_error err -> Printf.eprintf "Lazy: Got expected error: %s" (Json.error_to_string_hum err);
180179
end;
181180
[%expect {|
182181
`Fst: 5

drivers/xml_light/xml_light.ml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,24 @@ open Base
33
open Protocol_conv.Runtime
44
type t = Xml.xml
55

6-
exception Protocol_error of string * t option
7-
let () = Printexc.register_printer
8-
(function Protocol_error (s, Some t) -> Some (s ^ ": " ^ (Xml.to_string t))
9-
| Protocol_error (s, None) -> Some (s)
10-
| _ -> None)
6+
type error = string * t option
7+
exception Protocol_error of error
8+
9+
let to_string_hum xml = Xml.to_string_fmt xml
10+
11+
let error_to_string_hum: error -> string = function
12+
| (s, Some t) -> Printf.sprintf "%s. T: '%s'" s (to_string_hum t)
13+
| (s, None) -> s
14+
15+
(* Register exception printer *)
16+
let () = Printexc.register_printer (function
17+
| Protocol_error err -> Some (error_to_string_hum err)
18+
| _ -> None)
19+
20+
let try_with: (t -> 'a) -> t -> ('a, error) result = fun f t ->
21+
match f t with
22+
| v -> Ok v
23+
| exception (Protocol_error e) -> Error e
1124

1225
let raise_errorf t fmt =
1326
Caml.Printf.kprintf (fun s -> raise (Protocol_error (s, t))) fmt
@@ -16,9 +29,6 @@ let wrap t f x = match f x with
1629
| v -> v
1730
| exception Helper.Protocol_error s -> raise (Protocol_error (s, Some t))
1831

19-
let to_string_hum xml =
20-
Xml.to_string_fmt xml
21-
2232
let element name t = Xml.Element (name, [], t)
2333

2434
let record_to_xml assoc =

ppx/ppx_protocol_conv.ml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,8 @@ let type_of_of_func ~loc driver ~as_result tydecl =
658658
let result_type = match as_result with
659659
| false -> typ
660660
| true ->
661-
ptyp_constr ~loc { loc; txt = Ldot (Ldot (Lident "Protocol_conv", "Runtime"), "or_error") } [typ]
661+
let error_typ = ptyp_constr ~loc { loc; txt = Ldot (driver, "error") } [] in
662+
ptyp_constr ~loc { loc; txt = Ldot (Ldot (Lident "Protocol_conv", "Runtime"), "result") } [typ; error_typ]
662663
in
663664
[%type: [%t ptyp_constr ~loc { loc; txt = Ldot (driver, "t")} [] ] -> [%t result_type]]
664665

@@ -753,16 +754,21 @@ let of_protocol_str_type_decls t rec_flag ~loc tydecls =
753754
pexp_ident ~loc { loc; txt = Lident (name_of_core_type ~prefix:"of" ct).txt })
754755
tdecl.ptype_params
755756
in
757+
(*
756758
let args =
757759
type_params @ [ pexp_ident ~loc { loc; txt = Lident "t"} ]
758760
|> List.map ~f:(fun e -> (Nolabel, e))
759761
in
760762
pexp_apply ~loc (pexp_ident ~loc { loc; txt = Lident of_p.txt}) args
763+
764+
*)
765+
let args =
766+
type_params
767+
|> List.map ~f:(fun e -> (Nolabel, e))
768+
in
769+
pexp_apply ~loc (pexp_ident ~loc { loc; txt = Lident of_p.txt}) args
761770
in
762-
[%expr fun t -> match [%e expr] with
763-
| v -> Protocol_conv.Runtime.ok v
764-
| exception exn -> Protocol_conv.Runtime.error exn
765-
]
771+
pexp_apply ~loc (driver_func t ~loc "try_with") [Nolabel, expr]
766772
in
767773
let (defs, err_defs, is_recursive) =
768774
let is_recursive_f = is_recursive tydecls rec_flag in

ppx/test/test_driver.ml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
open Base
21
open Protocol_conv
32
open Runtime
3+
open Base
4+
45
type t =
56
| Record of (string * t) list
67
| Variant of string * t list
@@ -18,13 +19,27 @@ type t =
1819
| Unit
1920
[@@deriving sexp]
2021

21-
exception Protocol_error of string * t option
22+
type error = string * t option
23+
exception Protocol_error of error
2224

2325
let to_string_hum t = sexp_of_t t |> Sexp.to_string_hum
26+
let error_to_string_hum: error -> string = function
27+
| (s, Some t) -> Printf.sprintf "%s. T: '%s'" s (to_string_hum t)
28+
| (s, None) -> s
29+
30+
(* Register exception printer *)
31+
let () = Caml.Printexc.register_printer (function
32+
| Protocol_error err -> Some (error_to_string_hum err)
33+
| _ -> None)
2434

2535
let raise_errorf t fmt =
2636
Caml.Printf.kprintf (fun s -> raise (Protocol_error (s, Some t))) fmt
2737

38+
let try_with: (t -> 'a) -> t -> ('a, error) Runtime.result = fun f t ->
39+
match f t with
40+
| v -> Ok v
41+
| exception (Protocol_error e) -> Error e
42+
2843
let to_variant: (t, 'a) Variant_in.t list -> t -> 'a = fun spec -> function
2944
| Variant (name, args) -> Helper.to_variant spec name args
3045
| t -> raise_errorf t "Variant expected"

runtime/runtime.ml

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
open Base
22

3-
type 'a or_error = ('a, exn) Result.t
4-
let error exn = Result.Error exn
5-
let ok v = Result.Ok v
3+
type ('v, 'e) result = ('v, 'e) Result.t
64

75
module Record_in = struct
86
type (_, _, _) t =
@@ -36,15 +34,40 @@ module Variant_in = struct
3634
type (_, _) t = Variant: string * ('a, 'constr, 'c) Tuple_in.t * 'constr -> ('a, 'c) t
3735
end
3836

37+
(** Signature for a driver. Serialization function are on the form [of_XXX] and
38+
deserialization function are on the form [to_XXX].
39+
40+
All deserialization functions should only raise [Protocol_error] is the type could not be desrialized.
41+
*)
3942
module type Driver = sig
43+
44+
(** Serialized type. This type should not be opaque, so it is recommended that
45+
drivers implement the signature as [Runtime.Driver with type t = ... ]
46+
*)
4047
type t
41-
exception Protocol_error of string * t option
48+
49+
(** Opaque error type *)
50+
type error
51+
52+
(** Exception for protocol errors. The driver should make sure that
53+
this is the only exception raised when deserializing *)
54+
exception Protocol_error of error
55+
56+
(** Convert an error type to a human readable string *)
57+
val error_to_string_hum: error -> string
58+
59+
(** Convert t to a string *)
4260
val to_string_hum: t -> string
4361

62+
(** Wrap deserialization function to convert exceptions into an result type *)
63+
val try_with: (t -> 'v) -> t -> ('v, error) result
64+
4465
val to_variant: (t, 'a) Variant_in.t list -> t -> 'a
4566
val of_variant: string -> (t, 'a, t) Tuple_out.t -> 'a
67+
4668
val to_record: (t, 'constr, 'b) Record_in.t -> 'constr -> t -> 'b
4769
val of_record: (t, 'a, t) Record_out.t -> 'a
70+
4871
val to_tuple: (t, 'constr, 'b) Tuple_in.t -> 'constr -> t -> 'b
4972
val of_tuple: (t, 'a, t) Tuple_out.t -> 'a
5073

0 commit comments

Comments
 (0)