Source file error_monad.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
type error_category = [`Branch | `Temporary | `Permanent]
include (
TzCore : module type of TzCore with type error_category := error_category)
module TzTrace = TzTrace
type 'error trace = 'error TzTrace.trace
include TzMonad
type error += Exn of exn
let () =
register_error_kind
`Temporary
~id:"failure"
~title:"Exception"
~description:"Exception safely wrapped in an error"
~pp:(fun ppf s -> Format.fprintf ppf "@[<h 0>%a@]" Format.pp_print_text s)
Data_encoding.(obj1 (req "msg" string))
(function
| Exn (Failure msg) -> Some msg
| Exn exn -> Some (Printexc.to_string exn)
| _ -> None)
(fun msg -> Exn (Failure msg))
let error_with fmt =
Format.kasprintf (fun s -> Result_syntax.tzfail (Exn (Failure s))) fmt
let failwith fmt =
Format.kasprintf (fun s -> Lwt_result_syntax.tzfail (Exn (Failure s))) fmt
let error_of_exn e = Exn e
let trace_of_exn e = TzTrace.make @@ error_of_exn e
let error_with_exn e = Error (trace_of_exn e)
let fail_with_exn e = Lwt.return (error_with_exn e)
let error_of_fmt fmt = Format.kasprintf (fun str -> Exn (Failure str)) fmt
type error += Canceled
let () =
register_error_kind
`Temporary
~id:"canceled"
~title:"Canceled"
~description:"A promise was unexpectedly canceled"
~pp:(fun f () ->
Format.pp_print_string f "The promise was unexpectedly canceled")
Data_encoding.unit
(function Canceled -> Some () | _ -> None)
(fun () -> Canceled)
let protect_no_canceler ?on_error t =
let open Lwt_result_syntax in
let res = Lwt.catch t (fun exn -> tzfail (Exn exn)) in
let*! r = res in
match r with
| Ok _ -> res
| Error trace -> (
match on_error with
| None -> res
| Some on_error ->
Lwt.catch (fun () -> on_error trace) (fun exn -> tzfail (Exn exn)))
let protect_canceler ?on_error canceler t =
let open Lwt_result_syntax in
let cancellation =
let*! () = Lwt_canceler.when_canceling canceler in
tzfail Canceled
in
let res =
Lwt.pick [cancellation; Lwt.catch t (fun exn -> tzfail (Exn exn))]
in
let*! r = res in
match r with
| Ok _ -> res
| Error trace -> (
let trace =
if Lwt_canceler.canceled canceler then TzTrace.make Canceled else trace
in
match on_error with
| None -> Lwt.return_error trace
| Some on_error ->
Lwt.catch (fun () -> on_error trace) (fun exn -> tzfail (Exn exn)))
let protect ?on_error ?canceler t =
match canceler with
| None -> protect_no_canceler ?on_error t
| Some canceler -> protect_canceler ?on_error canceler t
type error += Timeout
let () =
register_error_kind
`Temporary
~id:"utils.Timeout"
~title:"Timeout"
~description:"Timeout"
~pp:(fun f () -> Format.pp_print_string f "The request has timed out")
Data_encoding.unit
(function Timeout -> Some () | _ -> None)
(fun () -> Timeout)
let with_timeout ?(canceler = Lwt_canceler.create ()) timeout f =
let open Lwt_result_syntax in
let target = f canceler in
let*! () =
Lwt.choose
[
timeout;
(let*! _ = target in
Lwt.return_unit);
]
in
if Lwt.state target <> Lwt.Sleep then (
Lwt.cancel timeout ;
target)
else
let*! r = Lwt_canceler.cancel canceler in
match r with
| Ok () | Error [] -> tzfail Timeout
| Error (h :: _) -> raise h
let errs_tag = Tag.def ~doc:"Errors" "errs" pp_print_trace
let cancel_with_exceptions canceler =
let open Lwt_syntax in
let* r = Lwt_canceler.cancel canceler in
match r with Ok () | Error [] -> Lwt.return_unit | Error (h :: _) -> raise h
let catch ?catch_only f = TzLwtreslib.Result.catch_f ?catch_only f trace_of_exn
let catch_e ?catch_only f =
TzLwtreslib.Result.catch_f ?catch_only f trace_of_exn |> Result.join
let catch_f ?catch_only f exc_mapper =
TzLwtreslib.Result.catch_f ?catch_only f (fun exc ->
TzTrace.make (exc_mapper exc))
let catch_s ?catch_only f =
let open Lwt_syntax in
let+ r = TzLwtreslib.Result.catch_s ?catch_only f in
Result.map_error trace_of_exn r
let catch_es ?catch_only f =
let open Lwt_syntax in
let+ r = TzLwtreslib.Result.catch_s ?catch_only f in
let r = Result.map_error trace_of_exn r in
Result.join r
let either_f (left : 'a tzresult Lwt.t) (right : unit -> 'a tzresult Lwt.t) =
let open Lwt_syntax in
let* l = left in
match l with
| Ok x -> return_ok x
| Error tr -> (
let* r = right () in
match r with Ok x -> return_ok x | Error e -> return_error (tr @ e))
let protect_result_canceler canceler t =
let open Lwt_syntax in
TzLwtreslib.Result.catch_s @@ fun () ->
let cancellation =
let* () = Lwt_canceler.when_canceling canceler in
raise Lwt.Canceled
in
Lwt.pick [cancellation; t ()]
let protect_result ?canceler t =
match canceler with
| None -> TzLwtreslib.Result.catch_s t
| Some canceler -> protect_result_canceler canceler t