src/error.bend
bendonnect-rpc@0.1.0.0 · 498 lines · 66 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14import Base
15import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes
16import bend-kit-encoding@0.4.0.0/encoding.bend as Enc
17import bend-kit-json@0.6.1.1/json.bend as Json
18import bendonnect-rpc@0.1.0.0/src/code.bend as Code
19import bendonnect-rpc@0.1.0.0/src/b64.bend as B64
20
21type Detail is Type:
22 Detail{type_name: String, value: Bytes.Bytes, debug: Maybe<&1, Json.Val>}
23
24type Error is Type:
25 Error{code: Code.Code, message: String, details: List<&1, Detail>}
26
27type EndStream is Type:
28 EndStream{error: Maybe<&1, Error>, metadata: Map<&2, List<&2, String>>}
29
30def new(code: Code.Code, message: String) -> Error:
31 Error{code, message, Nil{}}
32
33
34
35def str_val(+s: String) -> Json.Val:
36 Json.Str{Enc.utf8.encode(s)}
37
38def kv(+k: String, v: Json.Val) -> Bytes.Bytes & Json.Val:
39 (Enc.utf8.encode(k), v)
40
41
42
43def debug_part(m: Maybe<&1, Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
44 match m:
45 case None{}:
46 Nil{}
47 case Some{v}:
48 Con{kv("debug", v), Nil{}}
49
50def detail_to_json(d: Detail) -> Json.Val:
51 Detail{+ty, value, debug} = d
52 Json.Obj{Con{kv("type", str_val(ty)), Con{kv("value", str_val(B64.encode_std_raw(value))), debug_part(debug)}}}
53
54def details_json(ds: List<&1, Detail>) -> List<&1, Json.Val>:
55 match ds:
56 case Nil{}:
57 Nil{}
58 case Con{h, t}:
59 Con{detail_to_json(h), details_json(t)}
60
61def details_part(ds: List<&1, Detail>, rest: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
62 match ds:
63 case Nil{}:
64 rest
65 case Con{h, t}:
66 Con{kv("details", Json.Arr{Con{detail_to_json(h), details_json(t)}}), rest}
67
68def message_part.go(empty: Bool, m: String, rest: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
69 match empty:
70 case True{}:
71 rest
72 case False{}:
73 Con{kv("message", str_val(m)), rest}
74
75def message_part(+m: String, rest: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
76 message_part.go(String.is_empty(m), m, rest)
77
78def to_json(e: Error) -> Json.Val:
79 Error{code, message, details} = e
80 Json.Obj{Con{kv("code", str_val(Code.to_string(code))), message_part(message, details_part(details, Nil{}))}}
81
82def encode(e: Error) -> Bytes.Bytes:
83 Json.encode.bytes(to_json(e))
84
85
86
87
88type Fs is Type:
89 Fs{a: Maybe<&1, Json.Val>, b: Maybe<&1, Json.Val>, c: Maybe<&1, Json.Val>}
90
91def fs.empty() -> Fs:
92 Fs{None{}, None{}, None{}}
93
94def put1(v: Json.Val, f: Fs) -> Fs:
95 Fs{a, b, c} = f
96 Fs{Some{v}, b, c}
97
98def put2(v: Json.Val, f: Fs) -> Fs:
99 Fs{a, b, c} = f
100 Fs{a, Some{v}, c}
101
102def put3(v: Json.Val, f: Fs) -> Fs:
103 Fs{a, b, c} = f
104 Fs{a, b, Some{v}}
105
106def pick.put3(i3: Bool, v: Json.Val, f: Fs) -> Fs:
107 match i3:
108 case True{}:
109 put3(v, f)
110 case False{}:
111 f
112
113def pick.put2(i2: Bool, i3: Bool, v: Json.Val, f: Fs) -> Fs:
114 match i2:
115 case True{}:
116 put2(v, f)
117 case False{}:
118 pick.put3(i3, v, f)
119
120def pick.put(i1: Bool, i2: Bool, i3: Bool, v: Json.Val, f: Fs) -> Fs:
121 match i1:
122 case True{}:
123 put1(v, f)
124 case False{}:
125 pick.put2(i2, i3, v, f)
126
127def pick.slot(+ks: String, +n1: String, +n2: String, +n3: String, v: Json.Val, f: Fs) -> Fs:
128 pick.put(String.eq(ks, n1), String.eq(ks, n2), String.eq(ks, n3), v, f)
129
130def pick.go(kvs: List<&1, Bytes.Bytes & Json.Val>, +n1: String, +n2: String, +n3: String, f: Fs) -> Fs:
131 match kvs:
132 case Nil{}:
133 f
134 case Con{p, rest}:
135 (k, v) = p
136 pick.go(rest, n1, n2, n3, pick.slot(Enc.utf8.decode(k), n1, n2, n3, v, f))
137
138def unknown_of(m: Maybe<&2, Code.Code>) -> Code.Code:
139 match m:
140 case None{}:
141 Code.Unknown{}
142 case Some{c}:
143 c
144
145def code_str(s: Bytes.Bytes) -> Maybe<&2, Code.Code>:
146 Some{unknown_of(Code.from_string(Enc.utf8.decode(s)))}
147
148def code_of.val(v: Json.Val) -> Maybe<&2, Code.Code>:
149 match v:
150 case Json.Str{s}:
151 code_str(s)
152 case Json.Null{}:
153 None{}
154 case Json.Flag{on}:
155 None{}
156 case Json.Num{s}:
157 None{}
158 case Json.Arr{xs}:
159 None{}
160 case Json.Obj{kvs}:
161 None{}
162
163
164def code_of(m: Maybe<&1, Json.Val>) -> Maybe<&2, Code.Code>:
165 match m:
166 case None{}:
167 None{}
168 case Some{v}:
169 code_of.val(v)
170
171def str_of.val(v: Json.Val) -> String:
172 match v:
173 case Json.Str{s}:
174 Enc.utf8.decode(s)
175 case Json.Null{}:
176 ""
177 case Json.Flag{on}:
178 ""
179 case Json.Num{s}:
180 ""
181 case Json.Arr{xs}:
182 ""
183 case Json.Obj{kvs}:
184 ""
185
186def str_of(m: Maybe<&1, Json.Val>) -> String:
187 match m:
188 case None{}:
189 ""
190 case Some{v}:
191 str_of.val(v)
192
193def opt_str.val(v: Json.Val) -> Maybe<&2, String>:
194 match v:
195 case Json.Str{s}:
196 Some{Enc.utf8.decode(s)}
197 case Json.Null{}:
198 None{}
199 case Json.Flag{on}:
200 None{}
201 case Json.Num{s}:
202 None{}
203 case Json.Arr{xs}:
204 None{}
205 case Json.Obj{kvs}:
206 None{}
207
208def opt_str(m: Maybe<&1, Json.Val>) -> Maybe<&2, String>:
209 match m:
210 case None{}:
211 None{}
212 case Some{v}:
213 opt_str.val(v)
214
215def opt_b64(m: Maybe<&2, String>) -> Maybe<&1, Bytes.Bytes>:
216 match m:
217 case None{}:
218 None{}
219 case Some{+s}:
220 B64.decode(s)
221
222def detail_build(ty: Maybe<&2, String>, vb: Maybe<&1, Bytes.Bytes>, debug: Maybe<&1, Json.Val>) -> Maybe<&1, Detail>:
223 match ty vb:
224 case Some{t} Some{b}:
225 Some{Detail{t, b, debug}}
226 case _ _:
227 None{}
228
229def detail_fields(f: Fs) -> Maybe<&1, Detail>:
230 Fs{a, b, c} = f
231 detail_build(opt_str(a), opt_b64(opt_str(b)), c)
232
233def detail_of(v: Json.Val) -> Maybe<&1, Detail>:
234 match v:
235 case Json.Obj{kvs}:
236 detail_fields(pick.go(kvs, "type", "value", "debug", fs.empty()))
237 case Json.Null{}:
238 None{}
239 case Json.Flag{on}:
240 None{}
241 case Json.Num{s}:
242 None{}
243 case Json.Str{s}:
244 None{}
245 case Json.Arr{xs}:
246 None{}
247
248def details_push(m: Maybe<&1, Detail>, rest: List<&1, Detail>) -> List<&1, Detail>:
249 match m:
250 case Some{d}:
251 Con{d, rest}
252 case None{}:
253 rest
254
255def details_go(xs: List<&1, Json.Val>) -> List<&1, Detail>:
256 match xs:
257 case Nil{}:
258 Nil{}
259 case Con{h, t}:
260 details_push(detail_of(h), details_go(t))
261
262def details_val(v: Json.Val) -> List<&1, Detail>:
263 match v:
264 case Json.Arr{xs}:
265 details_go(xs)
266 case Json.Null{}:
267 Nil{}
268 case Json.Flag{on}:
269 Nil{}
270 case Json.Num{s}:
271 Nil{}
272 case Json.Str{s}:
273 Nil{}
274 case Json.Obj{kvs}:
275 Nil{}
276
277def details_of(m: Maybe<&1, Json.Val>) -> List<&1, Detail>:
278 match m:
279 case None{}:
280 Nil{}
281 case Some{v}:
282 details_val(v)
283
284def error_build(c: Maybe<&2, Code.Code>, msg: String, ds: List<&1, Detail>) -> Maybe<&1, Error>:
285 match c:
286 case None{}:
287 None{}
288 case Some{code}:
289 Some{Error{code, msg, ds}}
290
291def error_fields(f: Fs) -> Maybe<&1, Error>:
292 Fs{a, b, c} = f
293 error_build(code_of(a), str_of(b), details_of(c))
294
295
296def from_json(v: Json.Val) -> Maybe<&1, Error>:
297 match v:
298 case Json.Obj{kvs}:
299 error_fields(pick.go(kvs, "code", "message", "details", fs.empty()))
300 case Json.Null{}:
301 None{}
302 case Json.Flag{on}:
303 None{}
304 case Json.Num{s}:
305 None{}
306 case Json.Str{s}:
307 None{}
308 case Json.Arr{xs}:
309 None{}
310
311def decode.val(m: Maybe<&1, Json.Val>) -> Maybe<&1, Error>:
312 match m:
313 case None{}:
314 None{}
315 case Some{v}:
316 from_json(v)
317
318def decode(b: Bytes.Bytes) -> Maybe<&1, Error>:
319 decode.val(Json.parse.bytes(b))
320
321
322
323def status_text(+s: U32) -> String:
324 Bool.pick(String, U32.is_eq(s, 400), "Bad Request",
325 Bool.pick(String, U32.is_eq(s, 401), "Unauthorized",
326 Bool.pick(String, U32.is_eq(s, 403), "Forbidden",
327 Bool.pick(String, U32.is_eq(s, 404), "Not Found",
328 Bool.pick(String, U32.is_eq(s, 429), "Too Many Requests",
329 Bool.pick(String, U32.is_eq(s, 500), "Internal Server Error",
330 Bool.pick(String, U32.is_eq(s, 502), "Bad Gateway",
331 Bool.pick(String, U32.is_eq(s, 503), "Service Unavailable",
332 Bool.pick(String, U32.is_eq(s, 504), "Gateway Timeout",
333 String.append("HTTP ", U32.show(s)))))))))))
334
335def from_http.pick(m: Maybe<&1, Error>, +status: U32) -> Error:
336 match m:
337 case Some{e}:
338 e
339 case None{}:
340 Error{Code.from_http(status), status_text(status), Nil{}}
341
342
343
344def from_http(+status: U32, body: Bytes.Bytes) -> Error:
345 from_http.pick(decode(body), status)
346
347
348
349def strs_val(vs: List<&2, String>) -> List<&1, Json.Val>:
350 match vs:
351 case Nil{}:
352 Nil{}
353 case Con{h, t}:
354 Con{str_val(h), strs_val(t)}
355
356def meta_entries(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>) -> List<&1, Bytes.Bytes & Json.Val>:
357 match xs:
358 case Nil{}:
359 Nil{}
360 case Con{Tuple{+k, v}, rest}:
361 Con{kv(k, Json.Arr{strs_val(v)}), meta_entries(rest)}
362
363def meta_part(xs: List<&2, Sigma<&2, &2, String, _ => List<&2, String>>>) -> List<&1, Bytes.Bytes & Json.Val>:
364 match xs:
365 case Nil{}:
366 Nil{}
367 case Con{h, t}:
368 Con{kv("metadata", Json.Obj{meta_entries(Con{h, t})}), Nil{}}
369
370def err_part(e: Maybe<&1, Error>, rest: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
371 match e:
372 case None{}:
373 rest
374 case Some{x}:
375 Con{kv("error", to_json(x)), rest}
376
377def end_encode(e: EndStream) -> Bytes.Bytes:
378 EndStream{error, metadata} = e
379 Json.encode.bytes(Json.Obj{err_part(error, meta_part(Map.to_list(&2, List<&2, String>, metadata)))})
380
381def strs_push(v: Json.Val, rest: List<&2, String>) -> List<&2, String>:
382 match v:
383 case Json.Str{s}:
384 Con{Enc.utf8.decode(s), rest}
385 case Json.Null{}:
386 rest
387 case Json.Flag{on}:
388 rest
389 case Json.Num{s}:
390 rest
391 case Json.Arr{xs}:
392 rest
393 case Json.Obj{kvs}:
394 rest
395
396def strs_of(xs: List<&1, Json.Val>) -> List<&2, String>:
397 match xs:
398 case Nil{}:
399 Nil{}
400 case Con{h, t}:
401 strs_push(h, strs_of(t))
402
403def meta_set(+k: String, v: Json.Val, acc: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
404 match v:
405 case Json.Arr{xs}:
406 Map.set(&2, List<&2, String>, acc, String.to_lower(k), strs_of(xs))
407 case Json.Null{}:
408 acc
409 case Json.Flag{on}:
410 acc
411 case Json.Num{s}:
412 acc
413 case Json.Str{s}:
414 acc
415 case Json.Obj{kvs}:
416 acc
417
418def meta_map(kvs: List<&1, Bytes.Bytes & Json.Val>, acc: Map<&2, List<&2, String>>) -> Map<&2, List<&2, String>>:
419 match kvs:
420 case Nil{}:
421 acc
422 case Con{p, rest}:
423 (k, v) = p
424 meta_map(rest, meta_set(Enc.utf8.decode(k), v, acc))
425
426def meta_val(v: Json.Val) -> Maybe<&2, Map<&2, List<&2, String>>>:
427 match v:
428 case Json.Obj{kvs}:
429 Some{meta_map(kvs, Map.new(&2, List<&2, String>))}
430 case Json.Null{}:
431 Some{Map.new(&2, List<&2, String>)}
432 case Json.Flag{on}:
433 None{}
434 case Json.Num{s}:
435 None{}
436 case Json.Str{s}:
437 None{}
438 case Json.Arr{xs}:
439 None{}
440
441def meta_of(m: Maybe<&1, Json.Val>) -> Maybe<&2, Map<&2, List<&2, String>>>:
442 match m:
443 case None{}:
444 Some{Map.new(&2, List<&2, String>)}
445 case Some{v}:
446 meta_val(v)
447
448
449def err_val(m: Maybe<&1, Error>) -> Maybe<&1, Maybe<&1, Error>>:
450 match m:
451 case None{}:
452 None{}
453 case Some{e}:
454 Some{Some{e}}
455
456def err_of(m: Maybe<&1, Json.Val>) -> Maybe<&1, Maybe<&1, Error>>:
457 match m:
458 case None{}:
459 Some{None{}}
460 case Some{v}:
461 err_val(from_json(v))
462
463def end_build(e: Maybe<&1, Maybe<&1, Error>>, m: Maybe<&2, Map<&2, List<&2, String>>>) -> Maybe<&1, EndStream>:
464 match e m:
465 case Some{err} Some{md}:
466 Some{EndStream{err, md}}
467 case _ _:
468 None{}
469
470def end_fields(f: Fs) -> Maybe<&1, EndStream>:
471 Fs{a, b, c} = f
472 end_build(err_of(a), meta_of(b))
473
474def end_from_json(v: Json.Val) -> Maybe<&1, EndStream>:
475 match v:
476 case Json.Obj{kvs}:
477 end_fields(pick.go(kvs, "error", "metadata", "_", fs.empty()))
478 case Json.Null{}:
479 None{}
480 case Json.Flag{on}:
481 None{}
482 case Json.Num{s}:
483 None{}
484 case Json.Str{s}:
485 None{}
486 case Json.Arr{xs}:
487 None{}
488
489def end_decode.val(m: Maybe<&1, Json.Val>) -> Maybe<&1, EndStream>:
490 match m:
491 case None{}:
492 None{}
493 case Some{v}:
494 end_from_json(v)
495
496
497def end_decode(b: Bytes.Bytes) -> Maybe<&1, EndStream>:
498 end_decode.val(Json.parse.bytes(b))