tui/read.bend
cswap-bend-cli@0.1.0.0 · 265 lines · 52 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/store/seq.bend as SSeq
4import cswap-bend-cli@0.1.0.0/tui/model.bend as M
5import cswap-bend-cli@0.1.0.0/tui/num.bend as N
6
7
8
9
10
11
12
13def f_pid() -> String:
14 "pid"
15
16def f_next_poll() -> String:
17 "next_poll_at"
18
19def f_active() -> String:
20 "active"
21
22def f_accounts() -> String:
23 "accounts"
24
25def f_last_error() -> String:
26 "last_error"
27
28def f_backoff() -> String:
29 "backoff_until"
30
31
32
33def win_pct(+label: String, +w: Json.Json, +p: Maybe<&2, U32>) -> Maybe<&2, M.Win>:
34 match p:
35 case None{}:
36 None{}
37 case Some{c}:
38 Some{M.Win{label, p, N.iso_at("resets_at", w)}}
39
40
41def win_of(+label: String, +w: Json.Json) -> Maybe<&2, M.Win>:
42 win_pct(label, w, N.centi_at("pct", w))
43
44def win_opt(+label: String, +m: Maybe<&2, Json.Json>) -> Maybe<&2, M.Win>:
45 match m:
46 case None{}:
47 None{}
48 case Some{w}:
49 win_of(label, w)
50
51def win_key(+label: String, +key: String, +good: Json.Json) -> Maybe<&2, M.Win>:
52 win_opt(label, Json.get(key, good))
53
54def cons_win(+m: Maybe<&2, M.Win>, +rest: List<&2, M.Win>) -> List<&2, M.Win>:
55 match m:
56 case None{}:
57 rest
58 case Some{w}:
59 Con{w, rest}
60
61def scoped_win(+w: Json.Json) -> Maybe<&2, M.Win>:
62 win_of(N.str_at("name", w), w)
63
64def scoped(+items: List<&2, Json.Json>) -> List<&2, M.Win>:
65 match items:
66 case Nil{}:
67 Nil{}
68 case Con{w, t}:
69 cons_win(scoped_win(w), scoped(t))
70
71
72
73def wins_of(+good: Json.Json) -> List<&2, M.Win>:
74 cons_win(win_key("5h", "five_hour", good), cons_win(win_key("7d", "seven_day", good), scoped(Json.to_list(N.get_or_null("scoped", good)))))
75
76
77
78def age_of(+fetched: Maybe<&2, U32>, +now: U32) -> Maybe<&2, U32>:
79 match fetched:
80 case None{}:
81 None{}
82 case Some{t}:
83 Some{N.sel(U32.is_gt(now, t), (now - t : U32), 0)}
84
85def api_key(+rec: Json.Json) -> Bool:
86 String.eq(N.str_at("kind", rec), "api_key")
87
88def non_empty_pick(+blank: Bool, +s: String) -> Maybe<&2, String>:
89 match blank:
90 case True{}:
91 None{}
92 case False{}:
93 Some{s}
94
95def non_empty(+s: String) -> Maybe<&2, String>:
96 non_empty_pick(String.is_empty(s), s)
97
98def first_text(+a: String, +b: String) -> String:
99 N.str_or(non_empty(a), b)
100
101def error_of(+row: Json.Json, +st: Json.Json) -> String:
102 first_text(N.str_at("lastError", row), N.str_at(f_last_error(), st))
103
104def note_pick(+key: Bool, +err: String) -> String:
105 match key:
106 case True{}:
107 "API key (no quota)"
108 case False{}:
109 err
110
111def note_of(+rec: Json.Json, +row: Json.Json, +st: Json.Json, +wins: List<&2, M.Win>) -> String:
112 note_pick(api_key(rec), error_of(row, st))
113
114def flag_of(+m: Maybe<&2, Json.Json>) -> Bool:
115 match m:
116 case Some{Json.JBool{b}}:
117 b
118 case _:
119 False{}
120
121
122
123def flag(+rec: Json.Json, +key: String) -> Bool:
124 flag_of(Json.get(key, rec))
125
126def acct_with(+slot: String, +rec: Json.Json, +row: Json.Json, +st: Json.Json, +active: Bool, +now: U32, +wins: List<&2, M.Win>) -> M.Acct:
127 M.Acct{slot, N.str_at("email", rec), N.str_at("alias", rec), SSeq.org_tag(rec), active, flag(rec, "disabled"), wins, age_of(N.secs_at("fetchedAt", row), now), note_of(rec, row, st, wins)}
128
129def acct_of(+slot: String, +rec: Json.Json, +row: Json.Json, +st: Json.Json, +active: Bool, +now: U32) -> M.Acct:
130 acct_with(slot, rec, row, st, active, now, wins_of(N.get_or_null("lastGood", row)))
131
132def is_active(+slot: String, +active: Maybe<&2, U32>) -> Bool:
133 match active:
134 case None{}:
135 False{}
136 case Some{n}:
137 String.eq(slot, U32.show(n))
138
139def first_some(+a: Maybe<&2, U32>, +b: Maybe<&2, U32>) -> Maybe<&2, U32>:
140 match a:
141 case None{}:
142 b
143 case Some{x}:
144 a
145
146
147
148
149def active_slot(+status: Json.Json, +seq: Json.Json) -> Maybe<&2, U32>:
150 first_some(N.uint_at(f_active(), status), N.uint_at("activeAccountNumber", seq))
151
152def settings_block(+settings: Json.Json) -> Json.Json:
153 N.get_or_null("autoswitch", settings)
154
155def whole(+m: Maybe<&2, U32>) -> Maybe<&2, U32>:
156 match m:
157 case None{}:
158 None{}
159 case Some{c}:
160 Some{(c / 100 : U32)}
161
162def f_config() -> String:
163 "config"
164
165def f_interval() -> String:
166 "interval_seconds"
167
168def first_u(+a: Maybe<&2, U32>, +b: Maybe<&2, U32>) -> Maybe<&2, U32>:
169 first_some(a, b)
170
171
172
173def pick_j(+live: Bool, +j: Json.Json) -> Json.Json:
174 match live:
175 case True{}:
176 j
177 case False{}:
178 Json.JNull{}
179
180def daemon_cfg(+live: Bool, +status: Json.Json) -> Json.Json:
181 pick_j(live, N.get_or_null(f_config(), status))
182
183def thr_of(+live: Bool, +status: Json.Json, +settings: Json.Json) -> Maybe<&2, U32>:
184 first_u(N.centi_at("threshold", daemon_cfg(live, status)), N.centi_at("threshold", settings_block(settings)))
185
186def every_of(+live: Bool, +status: Json.Json, +settings: Json.Json) -> Maybe<&2, U32>:
187 first_u(N.uint_at(f_interval(), daemon_cfg(live, status)), whole(N.centi_at("intervalSeconds", settings_block(settings))))
188
189def hdr_of(+version: String, +live: Bool, +status: Json.Json, +settings: Json.Json) -> M.Hdr:
190 M.Hdr{version, live, N.uint_at(f_pid(), status), thr_of(live, status, settings), every_of(live, status, settings), N.iso_at(f_next_poll(), status)}
191
192
193
194
195def clock_part(+ts: String) -> String:
196 String.take(String.drop(ts, 11n), 8n)
197
198def join_pick(+empty: Bool, +a: String, +b: String) -> String:
199 match empty:
200 case True{}:
201 a
202 case False{}:
203 a ++ " " ++ b
204
205def join_sp(+a: String, +b: String) -> String:
206 join_pick(String.is_empty(b), a, b)
207
208def log_line(+j: Json.Json) -> String:
209 join_sp(join_sp(join_sp(clock_part(N.str_at("ts", j)) ++ " ", N.str_at("level", j)), N.str_at("msg", j)), N.str_at("outcome", j))
210
211def log_pick(+j: Json.Json, +line: String) -> String:
212 match j:
213 case Json.JObj{f}:
214 log_line(j)
215 case _:
216 String.trim(line)
217
218def log_text(+line: String) -> String:
219 log_pick(N.parse_text(line), line)
220
221def keep_line(+blank: Bool, +l: String, +rest: List<&2, String>) -> List<&2, String>:
222 match blank:
223 case True{}:
224 rest
225 case False{}:
226 Con{log_text(l), rest}
227
228
229
230def log_lines(+ls: List<&2, String>) -> List<&2, String>:
231 match ls:
232 case Nil{}:
233 Nil{}
234 case Con{l, t}:
235 keep_line(String.is_empty(String.trim(l)), l, log_lines(t))
236
237def acct_opt(+slot: String, +rec: Maybe<&2, Json.Json>, +cache: Json.Json, +status: Json.Json, +active: Maybe<&2, U32>, +now: U32) -> Maybe<&2, M.Acct>:
238 match rec:
239 case None{}:
240 None{}
241 case Some{r}:
242 Some{acct_of(slot, r, N.get_or_null(slot, N.get_or_null(f_accounts(), cache)), N.get_or_null(slot, N.get_or_null(f_accounts(), status)), is_active(slot, active), now)}
243
244def acct_cons(+m: Maybe<&2, M.Acct>, +rest: List<&2, M.Acct>) -> List<&2, M.Acct>:
245 match m:
246 case None{}:
247 rest
248 case Some{a}:
249 Con{a, rest}
250
251def accts(+nums: List<&2, U32>, +seq: Json.Json, +cache: Json.Json, +status: Json.Json, +active: Maybe<&2, U32>, +now: U32) -> List<&2, M.Acct>:
252 match nums:
253 case Nil{}:
254 Nil{}
255 case Con{n, t}:
256 acct_cons(acct_opt(U32.show(n), Json.get(U32.show(n), SSeq.accounts_of(seq)), cache, status, active, now), accts(t, seq, cache, status, active, now))
257
258def snap_of(+version: String, +live: Bool, +now: U32, +off: U32, +seq: Json.Json, +cache: Json.Json, +status: Json.Json, +settings: Json.Json, +log_t: String) -> M.Snap:
259 M.Snap{now, off, hdr_of(version, live, status, settings), accts(SSeq.seq_nums(seq), seq, cache, status, active_slot(status, seq), now), log_lines(String.lines(log_t))}
260
261
262
263
264def snap(+version: String, +live: Bool, +now: U32, +off: U32, +seq_t: String, +cache_t: String, +status_t: String, +settings_t: String, +log_t: String) -> M.Snap:
265 snap_of(version, live, now, off, N.parse_text(seq_t), N.parse_text(cache_t), N.parse_text(status_t), N.parse_text(settings_t), log_t)