My agentic slop goes here. Not intended for anyone else!
1(** Centralized error handling for the Requests library *)
2
3let src = Logs.Src.create "requests.error" ~doc:"HTTP Request Errors"
4module Log = (val Logs.src_log src : Logs.LOG)
5
6(** {1 Exception Types} *)
7
8exception Timeout
9exception TooManyRedirects of { url: string; count: int; max: int }
10exception ConnectionError of string
11exception HTTPError of {
12 url: string;
13 status: int;
14 reason: string;
15 body: string option;
16 headers: Headers.t
17}
18exception AuthenticationError of string
19exception SSLError of string
20exception ProxyError of string
21exception EncodingError of string
22exception InvalidURL of string
23exception InvalidRequest of string
24
25(** {1 Error Type} *)
26
27type t = [
28 | `Timeout
29 | `TooManyRedirects of string * int * int (* url, count, max *)
30 | `ConnectionError of string
31 | `HTTPError of string * int * string * string option * Headers.t (* url, status, reason, body, headers *)
32 | `AuthenticationError of string
33 | `SSLError of string
34 | `ProxyError of string
35 | `EncodingError of string
36 | `InvalidURL of string
37 | `InvalidRequest of string
38 | `UnknownError of string
39]
40
41(** {1 Conversion Functions} *)
42
43let of_exn = function
44 | Timeout -> Some `Timeout
45 | TooManyRedirects { url; count; max } ->
46 Some (`TooManyRedirects (url, count, max))
47 | ConnectionError msg -> Some (`ConnectionError msg)
48 | HTTPError { url; status; reason; body; headers } ->
49 Some (`HTTPError (url, status, reason, body, headers))
50 | AuthenticationError msg -> Some (`AuthenticationError msg)
51 | SSLError msg -> Some (`SSLError msg)
52 | ProxyError msg -> Some (`ProxyError msg)
53 | EncodingError msg -> Some (`EncodingError msg)
54 | InvalidURL msg -> Some (`InvalidURL msg)
55 | InvalidRequest msg -> Some (`InvalidRequest msg)
56 | _ -> None
57
58let to_exn = function
59 | `Timeout -> Timeout
60 | `TooManyRedirects (url, count, max) ->
61 TooManyRedirects { url; count; max }
62 | `ConnectionError msg -> ConnectionError msg
63 | `HTTPError (url, status, reason, body, headers) ->
64 HTTPError { url; status; reason; body; headers }
65 | `AuthenticationError msg -> AuthenticationError msg
66 | `SSLError msg -> SSLError msg
67 | `ProxyError msg -> ProxyError msg
68 | `EncodingError msg -> EncodingError msg
69 | `InvalidURL msg -> InvalidURL msg
70 | `InvalidRequest msg -> InvalidRequest msg
71 | `UnknownError msg -> Failure msg
72
73let raise error = Stdlib.raise (to_exn error)
74
75(** {1 Combinators} *)
76
77let catch f =
78 try Ok (f ())
79 with
80 | exn ->
81 match of_exn exn with
82 | Some err -> Error err
83 | None -> Error (`UnknownError (Printexc.to_string exn))
84
85let catch_async f = catch f (* In Eio, regular catch works for async too *)
86
87let map f = function
88 | Ok x -> Ok (f x)
89 | Error e -> Error e
90
91let bind f = function
92 | Ok x -> f x
93 | Error e -> Error e
94
95let both a b =
96 match a, b with
97 | Ok x, Ok y -> Ok (x, y)
98 | Error e, _ -> Error e
99 | _, Error e -> Error e
100
101let get_exn = function
102 | Ok x -> x
103 | Error e -> raise e
104
105let get_or ~default = function
106 | Ok x -> x
107 | Error _ -> default
108
109let is_retryable = function
110 | `Timeout -> true
111 | `ConnectionError _ -> true
112 | `HTTPError (_, status, _, _, _) -> Status.is_retryable (Status.of_int status)
113 | `SSLError _ -> true
114 | `ProxyError _ -> true
115 | _ -> false
116
117let is_client_error = function
118 | `HTTPError (_, status, _, _, _) -> Status.is_client_error (Status.of_int status)
119 | `AuthenticationError _
120 | `InvalidURL _
121 | `InvalidRequest _ -> true
122 | _ -> false
123
124let is_server_error = function
125 | `HTTPError (_, status, _, _, _) -> Status.is_server_error (Status.of_int status)
126 | _ -> false
127
128
129(** {1 Pretty Printing} *)
130
131let pp ppf = function
132 | `Timeout ->
133 Format.fprintf ppf "@[<2>Request Timeout:@ The request timed out@]"
134 | `TooManyRedirects (url, count, max) ->
135 Format.fprintf ppf "@[<2>Too Many Redirects:@ Exceeded maximum redirects (%d/%d) for URL: %s@]"
136 count max url
137 | `ConnectionError msg ->
138 Format.fprintf ppf "@[<2>Connection Error:@ %s@]" msg
139 | `HTTPError (url, status, reason, body, _headers) ->
140 Format.fprintf ppf "@[<v>@[<2>HTTP Error %d (%s):@ URL: %s@]" status reason url;
141 Option.iter (fun b ->
142 Format.fprintf ppf "@,@[<2>Response Body:@ %s@]" b
143 ) body;
144 Format.fprintf ppf "@]"
145 | `AuthenticationError msg ->
146 Format.fprintf ppf "@[<2>Authentication Error:@ %s@]" msg
147 | `SSLError msg ->
148 Format.fprintf ppf "@[<2>SSL/TLS Error:@ %s@]" msg
149 | `ProxyError msg ->
150 Format.fprintf ppf "@[<2>Proxy Error:@ %s@]" msg
151 | `EncodingError msg ->
152 Format.fprintf ppf "@[<2>Encoding Error:@ %s@]" msg
153 | `InvalidURL msg ->
154 Format.fprintf ppf "@[<2>Invalid URL:@ %s@]" msg
155 | `InvalidRequest msg ->
156 Format.fprintf ppf "@[<2>Invalid Request:@ %s@]" msg
157 | `UnknownError msg ->
158 Format.fprintf ppf "@[<2>Unknown Error:@ %s@]" msg
159
160let pp_exn ppf exn =
161 match of_exn exn with
162 | Some err -> pp ppf err
163 | None -> Format.fprintf ppf "%s" (Printexc.to_string exn)
164
165let to_string error =
166 Format.asprintf "%a" pp error
167
168(** {1 Syntax Module} *)
169
170module Syntax = struct
171 let ( let* ) x f = bind f x
172 let ( let+ ) x f = map f x
173 let ( and* ) = both
174end