daemon/status.bend
cswap-bend-cli@0.1.0.0 · 100 lines · 18 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/fs.bend as Fs
4import cswap-bend-cli@0.1.0.0/fs/write.bend as FsW
5import cswap-bend-cli@0.1.0.0/act.bend as Act
6import cswap-bend-cli@0.1.0.0/daemon/config.bend as Cfg
7
8
9
10
11
12
13
14
15type Entry is Data:
16 Entry{slot: String, pct5: Maybe<&2, U32>, pct7: Maybe<&2, U32>, fetched_at: Maybe<&2, String>, backoff_until: Maybe<&2, String>, last_error: Maybe<&2, String>}
17
18type Status is Data:
19 Status{pid: U32, version: String, started_at: String, last_poll_at: Maybe<&2, String>, next_poll_at: Maybe<&2, String>, active: Maybe<&2, String>, entries: List<&2, Entry>, cfg: Cfg.Cfg}
20
21def status_path(+state_dir: String) -> String:
22 state_dir ++ "/daemon-status.json"
23
24def pick_s(+c: Bool, +x: String, +y: String) -> String:
25 match c:
26 case True{}:
27 x
28 case False{}:
29 y
30
31def two(+n: U32) -> String:
32 pick_s(U32.is_lt(n, 10), "0" ++ U32.show(n), U32.show(n))
33
34def frac(+c: U32) -> String:
35 pick_s(U32.is_eq((c % 10 : U32), 0), U32.show((c / 10 : U32)), two(c))
36
37def pct(+c: U32) -> Json.Json:
38 Json.JNum{U32.show((c / 100 : U32)) ++ "." ++ frac((c % 100 : U32))}
39
40def opt_pct(+m: Maybe<&2, U32>) -> Json.Json:
41 match m:
42 case Some{c}:
43 pct(c)
44 case None{}:
45 Json.JNull{}
46
47def opt_str(+m: Maybe<&2, String>) -> Json.Json:
48 match m:
49 case Some{s}:
50 Json.JStr{s}
51 case None{}:
52 Json.JNull{}
53
54def f(+k: String, +v: Json.Json, +rest: Json.Json) -> Json.Json:
55 Json.JField{k, v, rest}
56
57def entry_json(e: Entry) -> Json.Json:
58 match e:
59 case Entry{s, p5, p7, fa, bu, le}:
60 Json.JObj{f("pct_5h", opt_pct(p5), f("pct_7d", opt_pct(p7), f("fetched_at", opt_str(fa), f("backoff_until", opt_str(bu), f("last_error", opt_str(le), Json.JNil{})))))}
61
62def entry_slot(e: Entry) -> String:
63 match e:
64 case Entry{s, p5, p7, fa, bu, le}:
65 s
66
67def entries_chain(+es: List<&2, Entry>) -> Json.Json:
68 match es:
69 case Nil{}:
70 Json.JNil{}
71 case Con{e, t}:
72 f(entry_slot(e), entry_json(e), entries_chain(t))
73
74def opt_num(+m: Maybe<&2, String>) -> Json.Json:
75 match m:
76 case Some{s}:
77 Json.JNum{s}
78 case None{}:
79 Json.JNull{}
80
81def str_arr(+xs: List<&2, String>) -> List<&2, Json.Json>:
82 match xs:
83 case Nil{}:
84 Nil{}
85 case Con{h, t}:
86 Con{Json.JStr{h}, str_arr(t)}
87
88def cfg_json(+c: Cfg.Cfg) -> Json.Json:
89 Json.JObj{f("threshold", pct(Cfg.cfg_threshold(c)), f("interval_seconds", Json.JNum{U32.show(Cfg.cfg_interval(c))}, f("cooldown_seconds", Json.JNum{U32.show(Cfg.cfg_cooldown(c))},
90 f("strategy", Json.JStr{Cfg.cfg_strategy(c)}, f("models", Json.arr(str_arr(Cfg.cfg_models(c))), f("include_api_keys", Json.JBool{Cfg.cfg_include_api(c)}, Json.JNil{}))))))}
91
92def to_json(s: Status) -> Json.Json:
93 match s:
94 case Status{pid, version, started, last_poll, next_poll, active, es, cfg}:
95 Json.JObj{f("schemaVersion", Json.JNum{"1"}, f("pid", Json.JNum{U32.show(pid)}, f("version", Json.JStr{version}, f("started_at", Json.JStr{started},
96 f("last_poll_at", opt_str(last_poll), f("next_poll_at", opt_str(next_poll), f("active", opt_num(active), f("config", cfg_json(cfg), f("accounts", Json.JObj{entries_chain(es)}, Json.JNil{})))))))))}
97
98
99def write(+state_dir: String, s: Status) -> Act.of(Unit):
100 FsW.write_atomic(status_path(state_dir), Json.emit_pretty(to_json(s)), "600")