fmt/shape.bend
cswap-bend-cli@0.1.0.0 · 257 lines · 39 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/fmt/csv.bend as Csv
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19type Col is Data:
20 Col{h: String, p: List<&2, String>}
21
22type Leaf is Data:
23 Leaf{k: String, v: String}
24
25
26
27def or_null(m: Maybe<&2, Json.Json>) -> Json.Json:
28 match m:
29 case None{}:
30 Json.JNull{}
31 case Some{v}:
32 v
33
34def get_or_null(+j: Json.Json, +k: String) -> Json.Json:
35 or_null(Json.get(k, j))
36
37
38def path(ks: List<&2, String>, +j: Json.Json) -> Json.Json:
39 match ks:
40 case Nil{}:
41 j
42 case Con{k, t}:
43 path(t, get_or_null(j, k))
44
45def is_scalar(j: Json.Json) -> Bool:
46 match j:
47 case Json.JArr{x}:
48 False{}
49 case Json.JObj{x}:
50 False{}
51 case _:
52 True{}
53
54
55def scal(+j: Json.Json) -> String:
56 match j:
57 case Json.JNull{}:
58 ""
59 case Json.JBool{b}:
60 Json.bool.show(b)
61 case Json.JNum{t}:
62 t
63 case Json.JStr{s}:
64 s
65 case _:
66 Json.emit(j)
67
68def all_scalar(items: Json.Json) -> Bool:
69 match items:
70 case Json.JCons{h, t}:
71 is_scalar(h) && all_scalar(t)
72 case _:
73 True{}
74
75def sep_of(t: Json.Json) -> String:
76 match t:
77 case Json.JNil{}:
78 ""
79 case _:
80 ";"
81
82def joined(items: Json.Json) -> String:
83 match items:
84 case Json.JCons{h, +t}:
85 scal(h) ++ sep_of(t) ++ joined(t)
86 case _:
87 ""
88
89def cell.arr(ok: Bool, +items: Json.Json) -> String:
90 match ok:
91 case True{}:
92 joined(items)
93 case False{}:
94 Json.emit(Json.JArr{items})
95
96def cell(+j: Json.Json) -> String:
97 match j:
98 case Json.JArr{+items}:
99 cell.arr(all_scalar(items), items)
100 case _:
101 scal(j)
102
103
104
105def col(h: String, p: List<&2, String>) -> Col:
106 Col{h, p}
107
108def headers(cols: List<&2, Col>) -> List<&2, String>:
109 match cols:
110 case Nil{}:
111 []
112 case Con{Col{h, p}, t}:
113 h <> headers(t)
114
115def cells(+j: Json.Json, cols: List<&2, Col>) -> List<&2, String>:
116 match cols:
117 case Nil{}:
118 []
119 case Con{Col{h, p}, t}:
120 cell(path(p, j)) <> cells(j, t)
121
122def rows(items: List<&2, Json.Json>, +cols: List<&2, Col>) -> List<&2, List<&2, String>>:
123 match items:
124 case Nil{}:
125 []
126 case Con{+h, t}:
127 cells(h, cols) <> rows(t, cols)
128
129def table(+cols: List<&2, Col>, items: List<&2, Json.Json>) -> String:
130 Csv.table(headers(cols) <> rows(items, cols))
131
132def account_cols() -> List<&2, Col>:
133 [col("slot", ["number"]), col("email", ["email"]), col("org", ["organizationName"]), col("kind", ["usageStatus"]), col("active", ["active"]),
134 col("5h_pct", ["usage", "fiveHour", "pct"]), col("5h_reset", ["usage", "fiveHour", "resetsAt"]),
135 col("7d_pct", ["usage", "sevenDay", "pct"]), col("7d_reset", ["usage", "sevenDay", "resetsAt"]),
136 col("fetched_at", ["usageFetchedAt"]), col("error", ["usageError"])]
137
138def setting_cols() -> List<&2, Col>:
139 [col("key", ["key"]), col("value", ["value"]), col("is_set", ["isSet"])]
140
141def profile_cols() -> List<&2, Col>:
142 [col("name", ["name"]), col("builtin", ["builtin"]), col("stored", ["stored"]), col("active", ["active"]), col("strategy", ["strategy"]),
143 col("threshold", ["effective", "threshold"]), col("models", ["effective", "models"]), col("cooldown_s", ["effective", "cooldown_s"]), col("interval_s", ["effective", "interval_s"])]
144
145def event_cols() -> List<&2, Col>:
146 [col("event", ["event"]), col("ts", ["ts"]), col("profile", ["profile"]), col("strategy", ["strategy"]),
147 col("active_slot", ["active", "number"]), col("from_slot", ["from", "number"]), col("to_slot", ["to", "number"]),
148 col("trigger", ["trigger"]), col("reason", ["reason"]), col("detail", ["detail"]), col("message", ["message"]),
149 col("threshold", ["threshold"]), col("seconds", ["seconds"]), col("until", ["until"]),
150 col("slot", ["number"]), col("email", ["email"]), col("dry_run", ["dryRun"]), col("earliest_reset_at", ["earliestResetAt"])]
151
152
153def event_header() -> String:
154 Csv.row(headers(event_cols())) ++ "\n"
155
156
157def event_row(+e: Json.Json) -> String:
158 Csv.row(cells(e, event_cols()))
159
160
161
162def dotted(+prefix: String, +k: String) -> String:
163 Bool.pick(String, String.is_empty(prefix), k, prefix ++ "." ++ k)
164
165def is_empty_obj(f: Json.Json) -> Bool:
166 match f:
167 case Json.JNil{}:
168 True{}
169 case _:
170 False{}
171
172def leaf.pick(empty: Bool, prefix: String, rest: List<&2, Leaf>) -> List<&2, Leaf>:
173 match empty:
174 case True{}:
175 Leaf{prefix, "{}"} <> rest
176 case False{}:
177 rest
178
179
180def leaves(j: Json.Json, +prefix: String) -> List<&2, Leaf>:
181 match j:
182 case Json.JObj{+f}:
183 leaf.pick(is_empty_obj(f), prefix, leaves(f, prefix))
184 case Json.JField{k, v, +t}:
185 List.append(&2, Leaf, leaves(v, dotted(prefix, k)), leaves(t, prefix))
186 case Json.JNil{}:
187 []
188 case _:
189 Leaf{prefix, cell(j)} <> []
190
191def leaf_headers(ls: List<&2, Leaf>) -> List<&2, String>:
192 match ls:
193 case Nil{}:
194 []
195 case Con{Leaf{k, v}, t}:
196 k <> leaf_headers(t)
197
198def leaf_values(ls: List<&2, Leaf>) -> List<&2, String>:
199 match ls:
200 case Nil{}:
201 []
202 case Con{Leaf{k, v}, t}:
203 v <> leaf_values(t)
204
205def leaf_pairs(ls: List<&2, Leaf>) -> List<&2, List<&2, String>>:
206 match ls:
207 case Nil{}:
208 []
209 case Con{Leaf{k, v}, t}:
210 [k, v] <> leaf_pairs(t)
211
212def wide.of(+ls: List<&2, Leaf>) -> String:
213 Csv.table([leaf_headers(ls), leaf_values(ls)])
214
215
216def wide(+j: Json.Json) -> String:
217 wide.of(leaves(j, ""))
218
219
220def kv(+j: Json.Json) -> String:
221 Csv.table(["key", "value"] <> leaf_pairs(leaves(j, "")))
222
223
224
225def items_at(+j: Json.Json, +k: String) -> List<&2, Json.Json>:
226 Json.to_list(get_or_null(j, k))
227
228
229def status_rows(a: Json.Json) -> List<&2, Json.Json>:
230 match a:
231 case Json.JObj{f}:
232 [Json.set("active", Json.JBool{True{}}, Json.JObj{f})]
233 case _:
234 []
235
236def some(m: Maybe<&2, Json.Json>) -> Bool:
237 match m:
238 case Some{e}:
239 True{}
240 case None{}:
241 False{}
242
243def has_error(+j: Json.Json) -> Bool:
244 some(Json.get("error", j))
245
246def by_key(+key: String, +j: Json.Json) -> String:
247 Bool.pick(String, String.eq(key, "list"), table(account_cols(), items_at(j, "accounts")),
248 Bool.pick(String, String.eq(key, "status"), table(account_cols(), status_rows(get_or_null(j, "active"))),
249 Bool.pick(String, String.eq(key, "config-list"), table(setting_cols(), items_at(j, "settings")),
250 Bool.pick(String, String.eq(key, "profile-list"), table(profile_cols(), items_at(j, "profiles")),
251 Bool.pick(String, String.eq(key, "export"), kv(j), wide(j))))))
252
253
254
255
256def csv_of(+key: String, +j: Json.Json) -> String:
257 Bool.pick(String, has_error(j), wide(j), by_key(key, j))