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>. 8 import Base 9 import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes 10 import bend-kit-encoding@0.4.0.0/encoding.bend as Enc 11 import bend-kit-json@0.6.1.1/json.bend as Json 12 import bend-kit-protobuf@0.1.0.0/protobuf.bend as Pb 13 import ./b64.bend as B64 14 import ./pb.bend as Pbr 15 16 # ------------------------------------------------------------------ text helpers 17 18 def 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 21 def num(+s: String) -> Json.Val: 22 Json.Num{Bytes.from_string(s)} 23 24 def 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. 28 def 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 39 def 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 46 def val.double(+f: F32) -> Json.Val: 47 f32.fin(f32.kind(F32.bits(f)), f) 48 49 def val.float(+f: F32) -> Json.Val: 50 f32.fin(f32.kind(F32.bits(f)), f) 51 52 def val.int32(+x: U32) -> Json.Val: 53 num(i32.show(x)) 54 55 def val.sint32(+x: U32) -> Json.Val: 56 num(i32.show(x)) 57 58 def val.sfixed32(+x: U32) -> Json.Val: 59 num(i32.show(x)) 60 61 def val.uint32(+x: U32) -> Json.Val: 62 num(U32.show(x)) 63 64 def val.fixed32(+x: U32) -> Json.Val: 65 num(U32.show(x)) 66 67 # 64-bit integers are quoted decimal strings. 68 def val.int64(+w: Pb.Word64) -> Json.Val: 69 str(Pbr.word64.show_s(w)) 70 71 def val.sint64(+w: Pb.Word64) -> Json.Val: 72 str(Pbr.word64.show_s(w)) 73 74 def val.sfixed64(+w: Pb.Word64) -> Json.Val: 75 str(Pbr.word64.show_s(w)) 76 77 def val.uint64(+w: Pb.Word64) -> Json.Val: 78 str(Pbr.word64.show_u(w)) 79 80 def val.fixed64(+w: Pb.Word64) -> Json.Val: 81 str(Pbr.word64.show_u(w)) 82 83 def val.bool(+b: Bool) -> Json.Val: 84 Json.Flag{b} 85 86 def val.string(+s: String) -> Json.Val: 87 str(s) 88 89 # Standard base64 with padding. 90 def 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. 94 def 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 97 def val.enum(~name: @+n: U32 -> String, +x: U32) -> Json.Val: 98 val.enum.go(name(x), x) 99 100 # ------------------------------------------------------------------ writers 101 102 def entry(+key: String, v: Json.Val) -> Bytes.Bytes & Json.Val: 103 (Enc.utf8.encode(key), v) 104 105 def 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 108 def 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. 116 def 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). 120 def 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 123 def 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 126 def 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 129 def 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 132 def 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 135 def 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 138 def 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 141 def 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 144 def 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 147 def 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 150 def 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 153 def 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 156 def 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 159 def 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 162 def 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 165 def 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). 169 def 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. 173 def 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 176 def 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 179 def 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 182 def 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 185 def 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 188 def 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 191 def 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 194 def 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 197 def 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 200 def 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 203 def 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 206 def 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 209 def 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 212 def 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 215 def 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 218 def 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 223 def 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 230 def 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 237 def 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 240 def 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 243 def 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 246 def 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 249 def 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 252 def 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 255 def 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 258 def 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 261 def 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 264 def 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 267 def 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 270 def 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 273 def 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 276 def 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 279 def 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 282 def 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. 286 def 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: entries built with Pj.entry(Pj.key.K(k), value); omitted when empty. 294 def 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 302 def key.int32(+x: U32) -> String: 303 i32.show(x) 304 305 def key.sint32(+x: U32) -> String: 306 i32.show(x) 307 308 def key.sfixed32(+x: U32) -> String: 309 i32.show(x) 310 311 def key.uint32(+x: U32) -> String: 312 U32.show(x) 313 314 def key.fixed32(+x: U32) -> String: 315 U32.show(x) 316 317 def key.int64(+w: Pb.Word64) -> String: 318 Pbr.word64.show_s(w) 319 320 def key.sint64(+w: Pb.Word64) -> String: 321 Pbr.word64.show_s(w) 322 323 def key.sfixed64(+w: Pb.Word64) -> String: 324 Pbr.word64.show_s(w) 325 326 def key.uint64(+w: Pb.Word64) -> String: 327 Pbr.word64.show_u(w) 328 329 def key.fixed64(+w: Pb.Word64) -> String: 330 Pbr.word64.show_u(w) 331 332 def key.bool(+b: Bool) -> String: 333 Bool.pick(String, b, "true", "false") 334 335 def key.string(+s: String) -> String: 336 s 337 338 # ------------------------------------------------------------------ encode / parse 339 340 def encode(v: Json.Val) -> Bytes.Bytes: 341 Json.encode.bytes(v) 342 343 def 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 350 def parse(b: Bytes.Bytes) -> Result<&2, &1, String, Json.Val>: 351 parse.fin(Json.parse.bytes(b)) 352 353 # ------------------------------------------------------------------ reading objects 354 355 def 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. 364 def 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. 380 def 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. 396 type 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. 401 def 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 408 def is_key(+k: String, +jn: String, +pn: String) -> Bool: 409 String.eq(k, jn) || String.eq(k, pn) 410 411 def 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 420 def 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. 430 def 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 435 def 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 442 def 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 450 def 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. 459 def parse.int32(+s: String) -> Maybe<&2, U32>: 460 int32.fin(Pbr.word64.read_s(s)) 461 462 def parse.uint32(+s: String) -> Maybe<&2, U32>: 463 uint32.fin(Pbr.word64.read_u(s)) 464 465 def parse.int64(+s: String) -> Maybe<&2, Pb.Word64>: 466 Pbr.word64.read_s(s) 467 468 def parse.uint64(+s: String) -> Maybe<&2, Pb.Word64>: 469 Pbr.word64.read_u(s) 470 471 def 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. 478 def 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 493 def read.int32(v: Json.Val) -> Result<&2, &1, String, U32>: 494 read.scalar(~U32, ~parse.int32, 0, "expected an int32", v) 495 496 def read.sint32(v: Json.Val) -> Result<&2, &1, String, U32>: 497 read.scalar(~U32, ~parse.int32, 0, "expected an int32", v) 498 499 def read.sfixed32(v: Json.Val) -> Result<&2, &1, String, U32>: 500 read.scalar(~U32, ~parse.int32, 0, "expected an int32", v) 501 502 def read.uint32(v: Json.Val) -> Result<&2, &1, String, U32>: 503 read.scalar(~U32, ~parse.uint32, 0, "expected a uint32", v) 504 505 def read.fixed32(v: Json.Val) -> Result<&2, &1, String, U32>: 506 read.scalar(~U32, ~parse.uint32, 0, "expected a uint32", v) 507 508 def 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 511 def 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 514 def 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 517 def 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 520 def 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 523 def read.float(v: Json.Val) -> Result<&2, &1, String, F32>: 524 read.scalar(~F32, ~parse.float, 0.0, "expected a float", v) 525 526 def read.double(v: Json.Val) -> Result<&2, &1, String, F32>: 527 read.scalar(~F32, ~parse.float, 0.0, "expected a double", v) 528 529 def 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 544 def 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 559 def 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. 567 def 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. 583 def 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 598 def 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)). 605 def 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 622 def 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). 633 def 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 648 def read.repeated.double(v: Json.Val) -> Result<&2, &1, String, List<&2, F32>>: 649 read.repeated(~F32, ~read.double, v) 650 651 def read.repeated.float(v: Json.Val) -> Result<&2, &1, String, List<&2, F32>>: 652 read.repeated(~F32, ~read.float, v) 653 654 def read.repeated.int32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>: 655 read.repeated(~U32, ~read.int32, v) 656 657 def read.repeated.sint32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>: 658 read.repeated(~U32, ~read.sint32, v) 659 660 def read.repeated.sfixed32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>: 661 read.repeated(~U32, ~read.sfixed32, v) 662 663 def read.repeated.uint32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>: 664 read.repeated(~U32, ~read.uint32, v) 665 666 def read.repeated.fixed32(v: Json.Val) -> Result<&2, &1, String, List<&2, U32>>: 667 read.repeated(~U32, ~read.fixed32, v) 668 669 def read.repeated.int64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>: 670 read.repeated(~Pb.Word64, ~read.int64, v) 671 672 def read.repeated.sint64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>: 673 read.repeated(~Pb.Word64, ~read.sint64, v) 674 675 def read.repeated.sfixed64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>: 676 read.repeated(~Pb.Word64, ~read.sfixed64, v) 677 678 def read.repeated.uint64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>: 679 read.repeated(~Pb.Word64, ~read.uint64, v) 680 681 def read.repeated.fixed64(v: Json.Val) -> Result<&2, &1, String, List<&2, Pb.Word64>>: 682 read.repeated(~Pb.Word64, ~read.fixed64, v) 683 684 def read.repeated.bool(v: Json.Val) -> Result<&2, &1, String, List<&2, Bool>>: 685 read.repeated(~Bool, ~read.bool, v) 686 687 def read.repeated.string(v: Json.Val) -> Result<&2, &1, String, List<&2, String>>: 688 read.repeated(~String, ~read.string, v) 689 690 def 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 693 def 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) 697 def read.key.int32(+s: String) -> Result<&2, &1, String, U32>: 698 lift.m(U32, parse.int32(s), "invalid int32 map key") 699 700 def read.key.sint32(+s: String) -> Result<&2, &1, String, U32>: 701 lift.m(U32, parse.int32(s), "invalid int32 map key") 702 703 def read.key.sfixed32(+s: String) -> Result<&2, &1, String, U32>: 704 lift.m(U32, parse.int32(s), "invalid int32 map key") 705 706 def read.key.uint32(+s: String) -> Result<&2, &1, String, U32>: 707 lift.m(U32, parse.uint32(s), "invalid uint32 map key") 708 709 def read.key.fixed32(+s: String) -> Result<&2, &1, String, U32>: 710 lift.m(U32, parse.uint32(s), "invalid uint32 map key") 711 712 def read.key.int64(+s: String) -> Result<&2, &1, String, Pb.Word64>: 713 lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key") 714 715 def read.key.sint64(+s: String) -> Result<&2, &1, String, Pb.Word64>: 716 lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key") 717 718 def read.key.sfixed64(+s: String) -> Result<&2, &1, String, Pb.Word64>: 719 lift.m(Pb.Word64, parse.int64(s), "invalid int64 map key") 720 721 def read.key.uint64(+s: String) -> Result<&2, &1, String, Pb.Word64>: 722 lift.m(Pb.Word64, parse.uint64(s), "invalid uint64 map key") 723 724 def read.key.fixed64(+s: String) -> Result<&2, &1, String, Pb.Word64>: 725 lift.m(Pb.Word64, parse.uint64(s), "invalid uint64 map key") 726 727 def 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 731 def read.key.string(+s: String) -> Result<&2, &1, String, String>: 732 Done{s} 733 734 def 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 -> []. 747 def 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)