src/typed.bend

bend-kube-runtime@0.1.1.0 · 177 lines · 0 definitions

1import Base
2import bend-kube-runtime@0.1.1.0/src/json.bend as J
3
4# Typed
5# =====
6#
7# What generated types (tools/gen.py) decode and encode with: typed views
8# of JSON objects, as k8s-openapi and kube-derive give kube-rs. A decoder
9# answers the value or an error naming the field's path ("spec.ports[2]
10# .port: expected an integer"); an encoder builds the JSON back, leaving
11# out absent optional fields and empty lists, as omitempty does.
12
13def Ty.Res(-A: Data) -> Type:
14  Result<&2, &2, String, A>
15
16def Ty.at.go(path: String, key: String, root: Bool) -> String:
17  match root:
18    case True{}:
19      key
20    case False{}:
21      path ++ "." ++ key
22
23def Ty.at(+path: String, key: String) -> String:
24  Ty.at.go(path, key, String.is_empty(path))
25
26# Scalars
27# -------
28
29def Ty.str(j: J.Json, path: String) -> Result<&2, &2, String, String>:
30  match j:
31    case J.JStr{s}:
32      Done{s}
33    case _:
34      Fail{path ++ ": expected a string"}
35
36def Ty.bool(j: J.Json, path: String) -> Result<&2, &2, String, Bool>:
37  match j:
38    case J.JBool{b}:
39      Done{b}
40    case _:
41      Fail{path ++ ": expected a boolean"}
42
43def Ty.u32.read(m: Maybe<&2, U32>, path: String) -> Result<&2, &2, String, U32>:
44  match m:
45    case Some{n}:
46      Done{n}
47    case None{}:
48      Fail{path ++ ": expected an integer from 0 to 4294967295"}
49
50def Ty.u32(j: J.Json, path: String) -> Result<&2, &2, String, U32>:
51  match j:
52    case J.JNum{t}:
53      Ty.u32.read(U32.read(t), path)
54    case _:
55      Fail{path ++ ": expected an integer"}
56
57# A number kept as its text (int64, floats): lossless.
58def Ty.num(j: J.Json, path: String) -> Result<&2, &2, String, String>:
59  match j:
60    case J.JNum{t}:
61      Done{t}
62    case _:
63      Fail{path ++ ": expected a number"}
64
65# Anything, untyped (maps, preserved unknown fields).
66def Ty.json(j: J.Json, path: String) -> Result<&2, &2, String, J.Json>:
67  Done{j}
68
69def Ty.from_str(s: String) -> J.Json:
70  J.JStr{s}
71
72def Ty.from_bool(b: Bool) -> J.Json:
73  J.JBool{b}
74
75def Ty.from_u32(n: U32) -> J.Json:
76  J.JNum{U32.show(n)}
77
78def Ty.from_num(t: String) -> J.Json:
79  J.JNum{t}
80
81def Ty.from_json(j: J.Json) -> J.Json:
82  j
83
84# Fields
85# ------
86
87def Ty.req.go(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, +j: J.Json,
88  +path: String, missing: Bool) -> Result<&2, &2, String, A>:
89  match missing:
90    case True{}:
91      Fail{path ++ ": required"}
92    case False{}:
93      dec(j, path)
94
95# A required field.
96def Ty.req(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, +j: J.Json, +key: String,
97  +path: String) -> Result<&2, &2, String, A>:
98  Ty.req.go(~A, ~dec, J.Json.get(j, key), Ty.at(path, key), J.Json.is_null(J.Json.get(j, key)))
99
100def Ty.opt.go(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, +j: J.Json,
101  +path: String, missing: Bool) -> Result<&2, &2, String, Maybe<&2, A>>:
102  match missing:
103    case True{}:
104      Done{None{}}
105    case False{}:
106      Result.map(&2, &2, String, A, Maybe<&2, A>, x => Some{x}, dec(j, path))
107
108# An optional field: None when absent or null.
109def Ty.opt(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, +j: J.Json, +key: String,
110  +path: String) -> Result<&2, &2, String, Maybe<&2, A>>:
111  Ty.opt.go(~A, ~dec, J.Json.get(j, key), Ty.at(path, key), J.Json.is_null(J.Json.get(j, key)))
112
113def Ty.list.go(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>,
114  xs: List<&2, J.Json>, +path: String, +i: U32) -> Result<&2, &2, String, List<&2, A>>:
115  match xs:
116    case Nil{}:
117      Done{Nil{}}
118    case Con{x, t}:
119      do Result<&2, &2, String, List<&2, A>>:
120        h : A <- dec(x, path ++ "[" ++ U32.show(i) ++ "]")
121        r : List<&2, A> <- Ty.list.go(~A, ~dec, t, path, (i + 1 : U32))
122        return h <> r
123
124def Ty.list.of(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, j: J.Json,
125  +path: String) -> Result<&2, &2, String, List<&2, A>>:
126  match j:
127    case J.JNull{}:
128      Done{Nil{}}
129    case J.JArr{xs}:
130      Ty.list.go(~A, ~dec, J.Json.chain(xs), path, 0)
131    case _:
132      Fail{path ++ ": expected a list"}
133
134# A list field: empty when absent.
135def Ty.list(~A: Data, ~dec: J.Json -> String -> Result<&2, &2, String, A>, +j: J.Json, +key: String,
136  +path: String) -> Result<&2, &2, String, List<&2, A>>:
137  Ty.list.of(~A, ~dec, J.Json.get(j, key), Ty.at(path, key))
138
139# Building
140# --------
141
142def Ty.put(~A: Data, ~enc: A -> J.Json, key: String, x: A) -> Maybe<&2, J.KV>:
143  Some{J.KV{key, enc(x)}}
144
145def Ty.put_opt(~A: Data, ~enc: A -> J.Json, key: String, m: Maybe<&2, A>) -> Maybe<&2, J.KV>:
146  match m:
147    case Some{x}:
148      Some{J.KV{key, enc(x)}}
149    case None{}:
150      None{}
151
152def Ty.encs(~A: Data, ~enc: A -> J.Json, xs: List<&2, A>) -> List<&2, J.Json>:
153  match xs:
154    case Nil{}:
155      Nil{}
156    case Con{x, t}:
157      enc(x) <> Ty.encs(~A, ~enc, t)
158
159def Ty.put_list(~A: Data, ~enc: A -> J.Json, key: String, xs: List<&2, A>) -> Maybe<&2, J.KV>:
160  match xs:
161    case Nil{}:
162      None{}
163    case +ys:
164      Some{J.KV{key, J.Json.arr(Ty.encs(~A, ~enc, ys))}}
165
166def Ty.fields(ms: List<&2, Maybe<&2, J.KV>>) -> List<&2, J.KV>:
167  match ms:
168    case Nil{}:
169      Nil{}
170    case Con{Some{kv}, t}:
171      kv <> Ty.fields(t)
172    case Con{None{}, t}:
173      Ty.fields(t)
174
175# An object from its present fields.
176def Ty.obj(ms: List<&2, Maybe<&2, J.KV>>) -> J.Json:
177  J.Json.obj(Ty.fields(ms))