src/pbjson.bend

bendonnect-rpc@0.1.0.0 · 750 lines · 163 definitions

1# proto3 JSON runtime for generated code (alias Pj). Pure. Reference: docs/runtime.md.
2#
3# Writing: accumulate `List<&1, Bytes & Json.Val>` in REVERSE (each put conses) and finish with `Pj.obj`.
4# Reading: `Pj.fields(v)` gives the object's entries as `List<&1, String & Json.Val>`; `Pj.take`
5# removes one field (by lowerCamel json name or original proto name) and answers its value
6# (Json.Null{} when absent, which every reader maps to the default).
7# Readers answer Result<&2, &1, String, T>.
8import Base
9import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes
10import bend-kit-encoding@0.4.0.0/encoding.bend as Enc
11import bend-kit-json@0.6.1.1/json.bend as Json
12import bend-kit-protobuf@0.1.0.0/protobuf.bend as Pb
13import bendonnect-rpc@0.1.0.0/src/b64.bend as B64
14import bendonnect-rpc@0.1.0.0/src/pb.bend as Pbr
15
16# ------------------------------------------------------------------ text helpers
17
18def i32.show(+x: U32) -> String:
19  Bool.pick(String, U32.is_ge(x, 2147483648), String.append("-", U32.show((0 - x : U32))), U32.show(x))
20
21def num(+s: String) -> Json.Val:
22  Json.Num{Bytes.from_string(s)}
23
24def str(+s: String) -> Json.Val:
25  Json.Str{Enc.utf8.encode(s)}
26
27# Finite F32 -> JSON number; NaN / +-Infinity -> the strings the proto3 JSON spec names.
28def f32.fin(kind: U32, +f: F32) -> Json.Val:
29  match kind:
30    case 0:
31      num(F32.show(f))
32    case 1:
33      str("NaN")
34    case 2:
35      str("Infinity")
36    case _:
37      str("-Infinity")
38
39def f32.kind(+b: U32) -> U32:
40  Bool.pick(U32, U32.is_ne(((b >> 23n) .&. 255 : U32), 255), 0,
41  Bool.pick(U32, U32.is_ne((b .&. 8388607 : U32), 0), 1,
42  Bool.pick(U32, U32.is_zero((b >> 31n : U32)), 2, 3)))
43
44# ------------------------------------------------------------------ values
45
46def val.double(+f: F32) -> Json.Val:
47  f32.fin(f32.kind(F32.bits(f)), f)
48
49def val.float(+f: F32) -> Json.Val:
50  f32.fin(f32.kind(F32.bits(f)), f)
51
52def val.int32(+x: U32) -> Json.Val:
53  num(i32.show(x))
54
55def val.sint32(+x: U32) -> Json.Val:
56  num(i32.show(x))
57
58def val.sfixed32(+x: U32) -> Json.Val:
59  num(i32.show(x))
60
61def val.uint32(+x: U32) -> Json.Val:
62  num(U32.show(x))
63
64def val.fixed32(+x: U32) -> Json.Val:
65  num(U32.show(x))
66
67# 64-bit integers are quoted decimal strings.
68def val.int64(+w: Pb.Word64) -> Json.Val:
69  str(Pbr.word64.show_s(w))
70
71def val.sint64(+w: Pb.Word64) -> Json.Val:
72  str(Pbr.word64.show_s(w))
73
74def val.sfixed64(+w: Pb.Word64) -> Json.Val:
75  str(Pbr.word64.show_s(w))
76
77def val.uint64(+w: Pb.Word64) -> Json.Val:
78  str(Pbr.word64.show_u(w))
79
80def val.fixed64(+w: Pb.Word64) -> Json.Val:
81  str(Pbr.word64.show_u(w))
82
83def val.bool(+b: Bool) -> Json.Val:
84  Json.Flag{b}
85
86def val.string(+s: String) -> Json.Val:
87  str(s)
88
89# Standard base64 with padding.
90def val.bytes(+xs: List<&2, U32>) -> Json.Val:
91  str(B64.encode_std(Pbr.bytes.of_list(xs)))
92
93# name(n) is the enum value's name; "" (unknown number) prints as the number.
94def val.enum.go(+s: String, +x: U32) -> Json.Val:
95  Bool.pick(Json.Val, String.is_empty(s), num(i32.show(x)), str(s))
96
97def val.enum(~name: @+n: U32 -> String, +x: U32) -> Json.Val:
98  val.enum.go(name(x), x)
99
100# ------------------------------------------------------------------ writers
101
102def entry(+key: String, v: Json.Val) -> Bytes.Bytes & Json.Val:
103  (Enc.utf8.encode(key), v)
104
105def put.always.v(+name: String, v: Json.Val, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
106  Con{entry(name, v), acc}
107
108def put.skip(skip: Bool, +name: String, v: Json.Val, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
109  match skip:
110    case True{}:
111      acc
112    case False{}:
113      put.always.v(name, v, acc)
114
115# Finishes an accumulator built by put.*: restores field order.
116def obj(acc: List<&1, Bytes.Bytes & Json.Val>) -> Json.Val:
117  Json.Obj{List.reverse(&1, Bytes.Bytes & Json.Val, acc)}
118
119# `put.K.always`: always emitted (oneof members, proto3 optional).
120def put.double.always(+name: String, +f: F32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
121  put.always.v(name, val.double(f), acc)
122
123def put.float.always(+name: String, +f: F32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
124  put.always.v(name, val.float(f), acc)
125
126def put.int32.always(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
127  put.always.v(name, val.int32(x), acc)
128
129def put.sint32.always(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
130  put.always.v(name, val.sint32(x), acc)
131
132def put.sfixed32.always(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
133  put.always.v(name, val.sfixed32(x), acc)
134
135def put.uint32.always(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
136  put.always.v(name, val.uint32(x), acc)
137
138def put.fixed32.always(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
139  put.always.v(name, val.fixed32(x), acc)
140
141def put.int64.always(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
142  put.always.v(name, val.int64(w), acc)
143
144def put.sint64.always(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
145  put.always.v(name, val.sint64(w), acc)
146
147def put.sfixed64.always(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
148  put.always.v(name, val.sfixed64(w), acc)
149
150def put.uint64.always(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
151  put.always.v(name, val.uint64(w), acc)
152
153def put.fixed64.always(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
154  put.always.v(name, val.fixed64(w), acc)
155
156def put.bool.always(+name: String, +b: Bool, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
157  put.always.v(name, val.bool(b), acc)
158
159def put.string.always(+name: String, +s: String, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
160  put.always.v(name, val.string(s), acc)
161
162def put.bytes.always(+name: String, +xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
163  put.always.v(name, val.bytes(xs), acc)
164
165def put.enum.always(~name: @+n: U32 -> String, +jn: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
166  put.always.v(jn, val.enum(~name, x), acc)
167
168# A present message: v is the generated Msg.to_json(m).
169def put.message(+name: String, v: Json.Val, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
170  put.always.v(name, v, acc)
171
172# `put.K`: omitted when x is the proto3 default.
173def put.double(+name: String, +f: F32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
174  put.skip(U32.is_zero(F32.bits(f)), name, val.double(f), acc)
175
176def put.float(+name: String, +f: F32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
177  put.skip(U32.is_zero(F32.bits(f)), name, val.float(f), acc)
178
179def put.int32(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
180  put.skip(U32.is_zero(x), name, val.int32(x), acc)
181
182def put.sint32(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
183  put.skip(U32.is_zero(x), name, val.sint32(x), acc)
184
185def put.sfixed32(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
186  put.skip(U32.is_zero(x), name, val.sfixed32(x), acc)
187
188def put.uint32(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
189  put.skip(U32.is_zero(x), name, val.uint32(x), acc)
190
191def put.fixed32(+name: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
192  put.skip(U32.is_zero(x), name, val.fixed32(x), acc)
193
194def put.int64(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
195  put.skip(Pb.Word64.is_zero(w), name, val.int64(w), acc)
196
197def put.sint64(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
198  put.skip(Pb.Word64.is_zero(w), name, val.sint64(w), acc)
199
200def put.sfixed64(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
201  put.skip(Pb.Word64.is_zero(w), name, val.sfixed64(w), acc)
202
203def put.uint64(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
204  put.skip(Pb.Word64.is_zero(w), name, val.uint64(w), acc)
205
206def put.fixed64(+name: String, +w: Pb.Word64, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
207  put.skip(Pb.Word64.is_zero(w), name, val.fixed64(w), acc)
208
209def put.bool(+name: String, +b: Bool, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
210  put.skip(Bool.not(b), name, val.bool(b), acc)
211
212def put.string(+name: String, +s: String, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
213  put.skip(String.is_empty(s), name, val.string(s), acc)
214
215def put.bytes(+name: String, +xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
216  put.skip(List.is_empty(&2, U32, xs), name, val.bytes(xs), acc)
217
218def put.enum(~name: @+n: U32 -> String, +jn: String, +x: U32, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
219  put.skip(U32.is_zero(x), jn, val.enum(~name, x), acc)
220
221# --- repeated: omitted when empty
222
223def rep.arr(~T: Data, ~val: @+x: T -> Json.Val, xs: List<&2, T>) -> List<&1, Json.Val>:
224  match xs:
225    case Nil{}:
226      Nil{}
227    case Con{h, t}:
228      val(h) <> rep.arr(~T, ~val, t)
229
230def rep.put(~T: Data, ~val: @+x: T -> Json.Val, +name: String, xs: List<&2, T>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
231  match xs:
232    case Nil{}:
233      acc
234    case Con{h, t}:
235      put.always.v(name, Json.Arr{rep.arr(~T, ~val, Con{h, t})}, acc)
236
237def put.repeated.double(+name: String, xs: List<&2, F32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
238  rep.put(~F32, ~val.double, name, xs, acc)
239
240def put.repeated.float(+name: String, xs: List<&2, F32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
241  rep.put(~F32, ~val.float, name, xs, acc)
242
243def put.repeated.int32(+name: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
244  rep.put(~U32, ~val.int32, name, xs, acc)
245
246def put.repeated.sint32(+name: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
247  rep.put(~U32, ~val.sint32, name, xs, acc)
248
249def put.repeated.sfixed32(+name: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
250  rep.put(~U32, ~val.sfixed32, name, xs, acc)
251
252def put.repeated.uint32(+name: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
253  rep.put(~U32, ~val.uint32, name, xs, acc)
254
255def put.repeated.fixed32(+name: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
256  rep.put(~U32, ~val.fixed32, name, xs, acc)
257
258def put.repeated.int64(+name: String, xs: List<&2, Pb.Word64>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
259  rep.put(~Pb.Word64, ~val.int64, name, xs, acc)
260
261def put.repeated.sint64(+name: String, xs: List<&2, Pb.Word64>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
262  rep.put(~Pb.Word64, ~val.sint64, name, xs, acc)
263
264def put.repeated.sfixed64(+name: String, xs: List<&2, Pb.Word64>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
265  rep.put(~Pb.Word64, ~val.sfixed64, name, xs, acc)
266
267def put.repeated.uint64(+name: String, xs: List<&2, Pb.Word64>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
268  rep.put(~Pb.Word64, ~val.uint64, name, xs, acc)
269
270def put.repeated.fixed64(+name: String, xs: List<&2, Pb.Word64>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
271  rep.put(~Pb.Word64, ~val.fixed64, name, xs, acc)
272
273def put.repeated.bool(+name: String, xs: List<&2, Bool>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
274  rep.put(~Bool, ~val.bool, name, xs, acc)
275
276def put.repeated.string(+name: String, xs: List<&2, String>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
277  rep.put(~String, ~val.string, name, xs, acc)
278
279def put.repeated.bytes(+name: String, xs: List<&2, List<&2, U32>>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
280  rep.put(~List<&2, U32>, ~val.bytes, name, xs, acc)
281
282def put.repeated.enum(~name: @+n: U32 -> String, +jn: String, xs: List<&2, U32>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
283  rep.put(~U32, ~(+x => val.enum(~name, x)), jn, xs, acc)
284
285# vs: each element's Msg.to_json(m); omitted when empty.
286def put.repeated.message(+name: String, vs: List<&1, Json.Val>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
287  match vs:
288    case Nil{}:
289      acc
290    case Con{h, t}:
291      put.always.v(name, Json.Arr{Con{h, t}}, acc)
292
293# map<K,V>: entries built with Pj.entry(Pj.key.K(k), value); omitted when empty.
294def put.map(+name: String, entries: List<&1, Bytes.Bytes & Json.Val>, acc: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, Bytes.Bytes & Json.Val>:
295  match entries:
296    case Nil{}:
297      acc
298    case Con{h, t}:
299      put.always.v(name, Json.Obj{Con{h, t}}, acc)
300
301# map keys as JSON object keys
302def key.int32(+x: U32) -> String:
303  i32.show(x)
304
305def key.sint32(+x: U32) -> String:
306  i32.show(x)
307
308def key.sfixed32(+x: U32) -> String:
309  i32.show(x)
310
311def key.uint32(+x: U32) -> String:
312  U32.show(x)
313
314def key.fixed32(+x: U32) -> String:
315  U32.show(x)
316
317def key.int64(+w: Pb.Word64) -> String:
318  Pbr.word64.show_s(w)
319
320def key.sint64(+w: Pb.Word64) -> String:
321  Pbr.word64.show_s(w)
322
323def key.sfixed64(+w: Pb.Word64) -> String:
324  Pbr.word64.show_s(w)
325
326def key.uint64(+w: Pb.Word64) -> String:
327  Pbr.word64.show_u(w)
328
329def key.fixed64(+w: Pb.Word64) -> String:
330  Pbr.word64.show_u(w)
331
332def key.bool(+b: Bool) -> String:
333  Bool.pick(String, b, "true", "false")
334
335def key.string(+s: String) -> String:
336  s
337
338# ------------------------------------------------------------------ encode / parse
339
340def encode(v: Json.Val) -> Bytes.Bytes:
341  Json.encode.bytes(v)
342
343def parse.fin(m: Maybe<&1, Json.Val>) -> Result<&2, &1, String, Json.Val>:
344  match m:
345    case None{}:
346      Fail{"invalid JSON"}
347    case Some{v}:
348      Done{v}
349
350def parse(b: Bytes.Bytes) -> Result<&2, &1, String, Json.Val>:
351  parse.fin(Json.parse.bytes(b))
352
353# ------------------------------------------------------------------ reading objects
354
355def fields.go(kvs: List<&1, Bytes.Bytes & Json.Val>) -> List<&1, String & Json.Val>:
356  match kvs:
357    case Nil{}:
358      Nil{}
359    case Con{(k, v), t}:
360      Con{(Enc.utf8.decode(k), v), fields.go(t)}
361
362# The entries of a JSON object, keys decoded. A non-object (including null) is an error;
363# for singular message fields test for null first with Pj.read.opt.
364def fields(v: Json.Val) -> Result<&2, &1, String, List<&1, String & Json.Val>>:
365  match v:
366    case Json.Obj{kvs}:
367      Done{fields.go(kvs)}
368    case Json.Null{}:
369      Fail{"expected a JSON object, got null"}
370    case Json.Flag{on}:
371      Fail{"expected a JSON object, got a boolean"}
372    case Json.Num{s}:
373      Fail{"expected a JSON object, got a number"}
374    case Json.Str{s}:
375      Fail{"expected a JSON object, got a string"}
376    case Json.Arr{xs}:
377      Fail{"expected a JSON object, got an array"}
378
379# A map field's entries (keys as strings); null is an empty map.
380def read.map.entries(v: Json.Val) -> Result<&2, &1, String, List<&1, String & Json.Val>>:
381  match v:
382    case Json.Obj{kvs}:
383      Done{fields.go(kvs)}
384    case Json.Null{}:
385      Done{Nil{}}
386    case Json.Flag{on}:
387      Fail{"expected a JSON object for a map field"}
388    case Json.Num{s}:
389      Fail{"expected a JSON object for a map field"}
390    case Json.Str{s}:
391      Fail{"expected a JSON object for a map field"}
392    case Json.Arr{xs}:
393      Fail{"expected a JSON object for a map field"}
394
395# One accepted JSON key (json_name or proto_name) of a field and that field's number.
396type JKey is Data:
397  JKey{name: String, num: U32}
398
399# The number of the field whose accepted key equals k, or 0 for an unknown key. Typical use:
400# `M.set(m, Pj.dispatch(k, M.json_keys()), v)` where M.set destructures m, then matches on its U32 parameter.
401def dispatch(+k: String, table: List<&2, JKey>) -> U32:
402  match table:
403    case Nil{}:
404      0
405    case Con{JKey{+name, +num}, t}:
406      Bool.pick(U32, String.eq(k, name), num, dispatch(k, t))
407
408def is_key(+k: String, +jn: String, +pn: String) -> Bool:
409  String.eq(k, jn) || String.eq(k, pn)
410
411def take.step(hit: Bool, k: String, v: Json.Val, r: Json.Val & List<&1, String & Json.Val>) -> Json.Val & List<&1, String & Json.Val>:
412  match hit:
413    case True{}:
414      (f, rest) = r
415      (v, rest)
416    case False{}:
417      (f, rest) = r
418      (f, Con{(k, v), rest})
419
420def take.go(+jn: String, +pn: String, kvs: List<&1, String & Json.Val>) -> Json.Val & List<&1, String & Json.Val>:
421  match kvs:
422    case Nil{}:
423      (Json.Null{}, Nil{})
424    case Con{(+k, v), t}:
425      take.step(is_key(k, jn, pn), k, v, take.go(jn, pn, t))
426
427# Removes the field named json_name or proto_name and answers its value (Json.Null{} if absent,
428# which every Pj.read.* maps to the default) beside the remaining entries. Unknown keys simply stay
429# in the rest and can be dropped. A later duplicate of a matched key is removed too.
430def take(kvs: List<&1, String & Json.Val>, +json_name: String, +proto_name: String) -> Json.Val & List<&1, String & Json.Val>:
431  take.go(json_name, proto_name, kvs)
432
433# ------------------------------------------------------------------ scalar readers
434
435def lift.m(-T: Data, m: Maybe<&2, T>, what: String) -> Result<&2, &1, String, T>:
436  match m:
437    case None{}:
438      Fail{what}
439    case Some{x}:
440      Done{x}
441
442def int32.fin(m: Maybe<&2, Pb.Word64>) -> Maybe<&2, U32>:
443  match m:
444    case None{}:
445      None{}
446    case Some{w}:
447      Pb.Word64{+h, +l} = w
448      Bool.pick(Maybe<&2, U32>, U32.is_eq(h, (0 - (l >> 31n) : U32)), Some{l}, None{})
449
450def uint32.fin(m: Maybe<&2, Pb.Word64>) -> Maybe<&2, U32>:
451  match m:
452    case None{}:
453      None{}
454    case Some{w}:
455      Pb.Word64{+h, +l} = w
456      Bool.pick(Maybe<&2, U32>, U32.is_zero(h), Some{l}, None{})
457
458# Decimal text (optionally negative) that fits int32 / uint32 / int64 / uint64.
459def parse.int32(+s: String) -> Maybe<&2, U32>:
460  int32.fin(Pbr.word64.read_s(s))
461
462def parse.uint32(+s: String) -> Maybe<&2, U32>:
463  uint32.fin(Pbr.word64.read_u(s))
464
465def parse.int64(+s: String) -> Maybe<&2, Pb.Word64>:
466  Pbr.word64.read_s(s)
467
468def parse.uint64(+s: String) -> Maybe<&2, Pb.Word64>:
469  Pbr.word64.read_u(s)
470
471def parse.float(+s: String) -> Maybe<&2, F32>:
472  Bool.pick(Maybe<&2, F32>, String.eq(s, "NaN"), Some{Pbr.f32.of_bits(2143289344)},
473  Bool.pick(Maybe<&2, F32>, String.eq(s, "Infinity"), Some{Pbr.f32.of_bits(2139095040)},
474  Bool.pick(Maybe<&2, F32>, String.eq(s, "-Infinity"), Some{Pbr.f32.of_bits(4286578688)},
475  F32.read(s))))
476
477# null -> dflt; number or string -> parse(text); anything else is an error.
478def read.scalar(~T: Data, ~parse: @+s: String -> Maybe<&2, T>, +dflt: T, +what: String, v: Json.Val) -> Result<&2, &1, String, T>:
479  match v:
480    case Json.Null{}:
481      Done{dflt}
482    case Json.Num{b}:
483      lift.m(T, parse(Bytes.to_string(b)), what)
484    case Json.Str{b}:
485      lift.m(T, parse(Enc.utf8.decode(b)), what)
486    case Json.Flag{on}:
487      Fail{what}
488    case Json.Arr{xs}:
489      Fail{what}
490    case Json.Obj{kvs}:
491      Fail{what}
492
493def read.int32(v: Json.Val) -> Result<&2, &1, String, U32>:
494  read.scalar(~U32, ~parse.int32, 0, "expected an int32", v)
495
496def read.sint32(v: Json.Val) -> Result<&2, &1, String, U32>:
497  read.scalar(~U32, ~parse.int32, 0, "expected an int32", v)
498
499def read.sfixed32(v: Json.Val) -> Result<&2, &1, String, U32>:
500  read.scalar(~U32, ~parse.int32, 0, "expected an int32", v)
501
502def read.uint32(v: Json.Val) -> Result<&2, &1, String, U32>:
503  read.scalar(~U32, ~parse.uint32, 0, "expected a uint32", v)
504
505def read.fixed32(v: Json.Val) -> Result<&2, &1, String, U32>:
506  read.scalar(~U32, ~parse.uint32, 0, "expected a uint32", v)
507
508def read.int64(v: Json.Val) -> Result<&2, &1, String, Pb.Word64>:
509  read.scalar(~Pb.Word64, ~parse.int64, Pb.Word64{0, 0}, "expected an int64", v)
510
511def read.sint64(v: Json.Val) -> Result<&2, &1, String, Pb.Word64>:
512  read.scalar(~Pb.Word64, ~parse.int64, Pb.Word64{0, 0}, "expected an int64", v)
513
514def read.sfixed64(v: Json.Val) -> Result<&2, &1, String, Pb.Word64>:
515  read.scalar(~Pb.Word64, ~parse.int64, Pb.Word64{0, 0}, "expected an int64", v)
516
517def read.uint64(v: Json.Val) -> Result<&2, &1, String, Pb.Word64>:
518  read.scalar(~Pb.Word64, ~parse.uint64, Pb.Word64{0, 0}, "expected a uint64", v)
519
520def read.fixed64(v: Json.Val) -> Result<&2, &1, String, Pb.Word64>:
521  read.scalar(~Pb.Word64, ~parse.uint64, Pb.Word64{0, 0}, "expected a uint64", v)
522
523def read.float(v: Json.Val) -> Result<&2, &1, String, F32>:
524  read.scalar(~F32, ~parse.float, 0.0, "expected a float", v)
525
526def read.double(v: Json.Val) -> Result<&2, &1, String, F32>:
527  read.scalar(~F32, ~parse.float, 0.0, "expected a double", v)
528
529def read.bool(v: Json.Val) -> Result<&2, &1, String, Bool>:
530  match v:
531    case Json.Null{}:
532      Done{False{}}
533    case Json.Flag{on}:
534      Done{on}
535    case Json.Num{s}:
536      Fail{"expected a boolean"}
537    case Json.Str{s}:
538      Fail{"expected a boolean"}
539    case Json.Arr{xs}:
540      Fail{"expected a boolean"}
541    case Json.Obj{kvs}:
542      Fail{"expected a boolean"}
543
544def read.string(v: Json.Val) -> Result<&2, &1, String, String>:
545  match v:
546    case Json.Null{}:
547      Done{""}
548    case Json.Str{b}:
549      Done{Enc.utf8.decode(b)}
550    case Json.Flag{on}:
551      Fail{"expected a string"}
552    case Json.Num{s}:
553      Fail{"expected a string"}
554    case Json.Arr{xs}:
555      Fail{"expected a string"}
556    case Json.Obj{kvs}:
557      Fail{"expected a string"}
558
559def bytes.fin(m: Maybe<&1, Bytes.Bytes>) -> Result<&2, &1, String, List<&2, U32>>:
560  match m:
561    case None{}:
562      Fail{"invalid base64"}
563    case Some{b}:
564      Done{Pbr.bytes.to_list(b)}
565
566# Standard or URL-safe base64, padded or not.
567def read.bytes(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
568  match v:
569    case Json.Null{}:
570      Done{Nil{}}
571    case Json.Str{b}:
572      bytes.fin(B64.decode(Enc.utf8.decode(b)))
573    case Json.Flag{on}:
574      Fail{"expected a base64 string"}
575    case Json.Num{s}:
576      Fail{"expected a base64 string"}
577    case Json.Arr{xs}:
578      Fail{"expected a base64 string"}
579    case Json.Obj{kvs}:
580      Fail{"expected a base64 string"}
581
582# Enum by name (from_name = the generated E.from_name) or by number; null -> 0.
583def read.enum(~from_name: @+s: String -> Maybe<&2, U32>, v: Json.Val) -> Result<&2, &1, String, U32>:
584  match v:
585    case Json.Null{}:
586      Done{0}
587    case Json.Str{b}:
588      lift.m(U32, from_name(Enc.utf8.decode(b)), "unknown enum value")
589    case Json.Num{b}:
590      lift.m(U32, parse.int32(Bytes.to_string(b)), "invalid enum number")
591    case Json.Flag{on}:
592      Fail{"expected an enum name or number"}
593    case Json.Arr{xs}:
594      Fail{"expected an enum name or number"}
595    case Json.Obj{kvs}:
596      Fail{"expected an enum name or number"}
597
598def opt.some(~T: Data, ~read: Json.Val -> Result<&2, &1, String, T>, v: Json.Val) -> Result<&2, &1, String, Maybe<&2, T>>:
599  do Result<&2, &1, String, Maybe<&2, T>>:
600    x : T <- read(v)
601    return Some{x}
602
603# Presence-aware read for optional scalars, oneof members and singular messages:
604# null/absent -> None, otherwise Some(read(v)).
605def read.opt(~T: Data, ~read: Json.Val -> Result<&2, &1, String, T>, v: Json.Val) -> Result<&2, &1, String, Maybe<&2, T>>:
606  match v:
607    case Json.Null{}:
608      Done{None{}}
609    case Json.Flag{on}:
610      opt.some(~T, ~read, Json.Flag{on})
611    case Json.Num{s}:
612      opt.some(~T, ~read, Json.Num{s})
613    case Json.Str{s}:
614      opt.some(~T, ~read, Json.Str{s})
615    case Json.Arr{xs}:
616      opt.some(~T, ~read, Json.Arr{xs})
617    case Json.Obj{kvs}:
618      opt.some(~T, ~read, Json.Obj{kvs})
619
620# ------------------------------------------------------------------ repeated and maps
621
622def rep.read(~T: Data, ~read: Json.Val -> Result<&2, &1, String, T>, xs: List<&1, Json.Val>) -> Result<&2, &1, String, List<&2, T>>:
623  match xs:
624    case Nil{}:
625      Done{Nil{}}
626    case Con{h, t}:
627      do Result<&2, &1, String, List<&2, T>>:
628        x : T <- read(h)
629        rest : List<&2, T> <- rep.read(~T, ~read, t)
630        return x <> rest
631
632# null -> []; array -> each element through read (for messages pass ~Msg.from_json).
633def read.repeated(~T: Data, ~read: Json.Val -> Result<&2, &1, String, T>, v: Json.Val) -> Result<&2, &1, String, List<&2, T>>:
634  match v:
635    case Json.Null{}:
636      Done{Nil{}}
637    case Json.Arr{xs}:
638      rep.read(~T, ~read, xs)
639    case Json.Flag{on}:
640      Fail{"expected an array"}
641    case Json.Num{s}:
642      Fail{"expected an array"}
643    case Json.Str{s}:
644      Fail{"expected an array"}
645    case Json.Obj{kvs}:
646      Fail{"expected an array"}
647
648def read.repeated.double(v: Json.Val) -> Result<&2, &1, String, List<&2, F32>>:
649  read.repeated(~F32, ~read.double, v)
650
651def read.repeated.float(v: Json.Val) -> Result<&2, &1, String, List<&2, F32>>:
652  read.repeated(~F32, ~read.float, v)
653
654def read.repeated.int32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
655  read.repeated(~U32, ~read.int32, v)
656
657def read.repeated.sint32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
658  read.repeated(~U32, ~read.sint32, v)
659
660def read.repeated.sfixed32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
661  read.repeated(~U32, ~read.sfixed32, v)
662
663def read.repeated.uint32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
664  read.repeated(~U32, ~read.uint32, v)
665
666def read.repeated.fixed32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
667  read.repeated(~U32, ~read.fixed32, v)
668
669def read.repeated.int64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>:
670  read.repeated(~Pb.Word64, ~read.int64, v)
671
672def read.repeated.sint64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>:
673  read.repeated(~Pb.Word64, ~read.sint64, v)
674
675def read.repeated.sfixed64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>:
676  read.repeated(~Pb.Word64, ~read.sfixed64, v)
677
678def read.repeated.uint64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>:
679  read.repeated(~Pb.Word64, ~read.uint64, v)
680
681def read.repeated.fixed64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>:
682  read.repeated(~Pb.Word64, ~read.fixed64, v)
683
684def read.repeated.bool(v: Json.Val) -> Result<&2, &1, String, List<&2, Bool>>:
685  read.repeated(~Bool, ~read.bool, v)
686
687def read.repeated.string(v: Json.Val) -> Result<&2, &1, String, List<&2, String>>:
688  read.repeated(~String, ~read.string, v)
689
690def read.repeated.bytes(v: Json.Val) -> Result<&2, &1, String, List<&2, List<&2, U32>>>:
691  read.repeated(~List<&2, U32>, ~read.bytes, v)
692
693def read.repeated.enum(~from_name: @+s: String -> Maybe<&2, U32>, v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>:
694  read.repeated(~U32, ~(v => read.enum(~from_name, v)), v)
695
696# map keys (JSON object keys are strings)
697def read.key.int32(+s: String) -> Result<&2, &1, String, U32>:
698  lift.m(U32, parse.int32(s), "invalid int32 map key")
699
700def read.key.sint32(+s: String) -> Result<&2, &1, String, U32>:
701  lift.m(U32, parse.int32(s), "invalid int32 map key")
702
703def read.key.sfixed32(+s: String) -> Result<&2, &1, String, U32>:
704  lift.m(U32, parse.int32(s), "invalid int32 map key")
705
706def read.key.uint32(+s: String) -> Result<&2, &1, String, U32>:
707  lift.m(U32, parse.uint32(s), "invalid uint32 map key")
708
709def read.key.fixed32(+s: String) -> Result<&2, &1, String, U32>:
710  lift.m(U32, parse.uint32(s), "invalid uint32 map key")
711
712def read.key.int64(+s: String) -> Result<&2, &1, String, Pb.Word64>:
713  lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key")
714
715def read.key.sint64(+s: String) -> Result<&2, &1, String, Pb.Word64>:
716  lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key")
717
718def read.key.sfixed64(+s: String) -> Result<&2, &1, String, Pb.Word64>:
719  lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key")
720
721def read.key.uint64(+s: String) -> Result<&2, &1, String, Pb.Word64>:
722  lift.m(Pb.Word64, parse.uint64(s), "invalid uint64 map key")
723
724def read.key.fixed64(+s: String) -> Result<&2, &1, String, Pb.Word64>:
725  lift.m(Pb.Word64, parse.uint64(s), "invalid uint64 map key")
726
727def read.key.bool(+s: String) -> Result<&2, &1, String, Bool>:
728  Bool.pick(Result<&2, &1, String, Bool>, String.eq(s, "true"), Done{True{}},
729  Bool.pick(Result<&2, &1, String, Bool>, String.eq(s, "false"), Done{False{}}, Fail{"invalid bool map key"}))
730
731def read.key.string(+s: String) -> Result<&2, &1, String, String>:
732  Done{s}
733
734def map.go(~K: Data, ~V: Data, ~E: Data, ~key: @+s: String -> Result<&2, &1, String, K>, ~value: Json.Val -> Result<&2, &1, String, V>, ~mk: @+k: K -> @+v: V -> E, es: List<&1, String & Json.Val>) -> Result<&2, &1, String, List<&2, E>>:
735  match es:
736    case Nil{}:
737      Done{Nil{}}
738    case Con{(k, v), t}:
739      do Result<&2, &1, String, List<&2, E>>:
740        kk : K <- key(k)
741        vv : V <- value(v)
742        rest : List<&2, E> <- map.go(~K, ~V, ~E, ~key, ~value, ~mk, t)
743        return mk(kk, vv) <> rest
744
745# Whole map field in one call: keys via ~Pj.read.key.K, values via ~Pj.read.V (or ~Msg.from_json),
746# entries built by mk (e.g. ~(+k => +v => Entry{k, v})). null -> [].
747def read.map(~K: Data, ~V: Data, ~E: Data, ~key: @+s: String -> Result<&2, &1, String, K>, ~value: Json.Val -> Result<&2, &1, String, V>, ~mk: @+k: K -> @+v: V -> E, v: Json.Val) -> Result<&2, &1, String, List<&2, E>>:
748  do Result<&2, &1, String, List<&2, E>>:
749    es : List<&1, String & Json.Val> <- read.map.entries(v)
750    map.go(~K, ~V, ~E, ~key, ~value, ~mk, es)