setload.bend
cswap-bend-cli@0.1.0.0 · 339 lines · 61 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/fs/write.bend as FWrite
3import cswap-bend-cli@0.1.0.0/json.bend as Json
4import cswap-bend-cli@0.1.0.0/fs.bend as Fs
5import cswap-bend-cli@0.1.0.0/act.bend as Act
6import cswap-bend-cli@0.1.0.0/cmd.bend as Cmd
7import cswap-bend-cli@0.1.0.0/settings.bend as Settings
8
9def truthy.items(j: Json.Json) -> Bool:
10 match j:
11 case Json.JNil{}:
12 False{}
13 case _:
14 True{}
15
16
17def truthy(j: Json.Json) -> Bool:
18 match j:
19 case Json.JNull{}:
20 False{}
21 case Json.JBool{b}:
22 b
23 case Json.JNum{t}:
24 Bool.not(Settings.is_zero(Settings.lit(t)))
25 case Json.JStr{s}:
26 Bool.not(String.is_empty(s))
27 case Json.JArr{items}:
28 truthy.items(items)
29 case Json.JObj{fields}:
30 truthy.items(fields)
31 case _:
32 False{}
33
34def empty() -> Json.Json:
35 Json.of_pairs([])
36
37def obj_or_empty(j: Json.Json) -> Json.Json:
38 match j:
39 case Json.JObj{f}:
40 Json.JObj{f}
41 case _:
42 empty()
43
44def has_str(xs: List<&2, String>, +v: String) -> Bool:
45 match xs:
46 case Nil{}:
47 False{}
48 case Con{h, t}:
49 String.eq(h, v) || has_str(t, v)
50
51def get_or_null(+key: String, +j: Json.Json) -> Json.Json:
52 Maybe.default(&2, Json.Json, Json.get(key, j), Json.JNull{})
53
54def int_text(d: Settings.Dec) -> String:
55 match d:
56 case Settings.Dec{neg, i, f}:
57 i
58
59def norm.show(+isint: Bool, +d: Settings.Dec) -> Json.Json:
60 Json.JNum{Settings.sel.s(isint, int_text(d), Settings.repr(d))}
61
62def norm.fin(m: Maybe<&2, Settings.Dec>, isint: Bool, lo: String, hi: String, dflt: Json.Json) -> Json.Json:
63 match m:
64 case Some{d}:
65 norm.show(isint, Settings.clamp(d, Settings.lit(lo), Settings.lit(hi)))
66 case None{}:
67 dflt
68
69def norm.numv(isint: Bool, lo: String, hi: String, dflt: Json.Json, v: Json.Json) -> Json.Json:
70 match v:
71 case Json.JNum{t}:
72 norm.fin(Settings.num(t), isint, lo, hi, dflt)
73 case _:
74 dflt
75
76def norm.pick(ok: Bool, v: Json.Json, dflt: Json.Json) -> Json.Json:
77 match ok:
78 case True{}:
79 v
80 case False{}:
81 dflt
82
83def norm.choice(choices: String, dflt: Json.Json, v: Json.Json) -> Json.Json:
84 match v:
85 case Json.JStr{+s}:
86 norm.pick(has_str(String.split(choices, ','), s), Json.JStr{s}, dflt)
87 case _:
88 dflt
89
90def norm.str(dflt: Json.Json, v: Json.Json) -> Json.Json:
91 match v:
92 case Json.JStr{+s}:
93 norm.pick(Bool.not(String.is_empty(s)), Json.JStr{s}, dflt)
94 case _:
95 dflt
96
97
98
99def norm.kind(kind: Settings.Sort, lo: String, hi: String, choices: String, dflt: Json.Json, v: Json.Json) -> Json.Json:
100 match kind:
101 case Settings.KFloat{}:
102 norm.numv(False{}, lo, hi, dflt, v)
103 case Settings.KInt{}:
104 norm.numv(True{}, lo, hi, dflt, v)
105 case Settings.KBool{}:
106 Json.JBool{truthy(v)}
107 case Settings.KChoice{}:
108 norm.choice(choices, dflt, v)
109 case Settings.KString{}:
110 norm.str(dflt, v)
111
112def norm.one2(hit: Bool, key: String, v: Json.Json, out: Json.Json) -> Json.Json:
113 match hit:
114 case True{}:
115 Json.set(key, v, out)
116 case False{}:
117 out
118
119def norm.one(sp: Settings.Spec, sec: String, src: Json.Json, out: Json.Json) -> Json.Json:
120 match sp:
121 case Settings.Spec{section, +key, kind, lo, hi, choices, dflt}:
122 norm.one2(String.eq(section, sec), key, norm.kind(kind, lo, hi, choices, dflt, get_or_null(key, src)), out)
123
124def norm.sec(xs: List<&2, Settings.Spec>, +sec: String, +src: Json.Json, out: Json.Json) -> Json.Json:
125 match xs:
126 case Nil{}:
127 out
128 case Con{h, t}:
129 norm.sec(t, sec, src, norm.one(h, sec, src, out))
130
131def sect_obj(m: Maybe<&2, Json.Json>) -> Json.Json:
132 match m:
133 case Some{j}:
134 obj_or_empty(j)
135 case None{}:
136 empty()
137
138
139def section_of(+raw: Json.Json, +sec: String) -> Json.Json:
140 sect_obj(Json.get(sec, raw))
141
142def norm.section2(+sec: String, base: Json.Json, +s: Json.Json) -> Json.Json:
143 Json.set(sec, norm.sec(Settings.specs(), sec, s, s), base)
144
145def norm.section(+sec: String, +base: Json.Json) -> Json.Json:
146 norm.section2(sec, base, section_of(base, sec))
147
148def ver.pick(m: Maybe<&2, Json.Json>, raw: Json.Json) -> Json.Json:
149 match m:
150 case Some{v}:
151 raw
152 case None{}:
153 Json.set("schemaVersion", Json.JNum{"1"}, raw)
154
155
156def with_version(+raw: Json.Json) -> Json.Json:
157 ver.pick(Json.get("schemaVersion", raw), raw)
158
159
160
161def normalize(raw: Json.Json) -> Json.Json:
162 norm.section("ui", norm.section("autoswitch", with_version(obj_or_empty(raw))))
163
164def path(+root: String) -> String:
165 root ++ "/settings.json"
166
167def raw.parsed(r: Result<&2, &2, String, Json.Json>) -> Json.Json:
168 match r:
169 case Fail{e}:
170 empty()
171 case Done{j}:
172 obj_or_empty(j)
173
174def raw.of(r: Fs.Res(String)) -> Json.Json:
175 match r:
176 case Fail{e}:
177 empty()
178 case Done{t}:
179 raw.parsed(Json.parse(t))
180
181
182
183def load_raw(+root: String) -> IO(Json.Json):
184 IO.bind(Fs.Res(String), Json.Json, Fs.read_file(path(root)), r => IO.pure(Json.Json, raw.of(r)))
185
186
187def load(+root: String) -> IO(Json.Json):
188 IO.bind(Json.Json, Json.Json, load_raw(root), raw => IO.pure(Json.Json, normalize(raw)))
189
190def autoswitch(+s: Json.Json, +key: String) -> Json.Json:
191 get_or_null(key, get_or_null("autoswitch", s))
192
193def text_of(j: Json.Json) -> String:
194 match j:
195 case Json.JNum{t}:
196 t
197 case _:
198 ""
199
200
201def threshold(s: Json.Json) -> String:
202 text_of(autoswitch(s, "threshold"))
203
204def interval_seconds(s: Json.Json) -> String:
205 text_of(autoswitch(s, "intervalSeconds"))
206
207def cooldown_seconds(s: Json.Json) -> String:
208 text_of(autoswitch(s, "cooldownSeconds"))
209
210def hysteresis_pct(s: Json.Json) -> String:
211 text_of(autoswitch(s, "hysteresisPct"))
212
213def strategy.of(j: Json.Json) -> String:
214 match j:
215 case Json.JStr{s}:
216 s
217 case _:
218 "best"
219
220def strategy(s: Json.Json) -> String:
221 strategy.of(autoswitch(s, "strategy"))
222
223def include_api_key_accounts(s: Json.Json) -> Bool:
224 truthy(autoswitch(s, "includeApiKeyAccounts"))
225
226def unhealthy_ticks(s: Json.Json) -> U32:
227 Maybe.default(&2, U32, U32.read(text_of(autoswitch(s, "unhealthyTicks"))), 3)
228
229
230def threshold_tenths(s: Json.Json) -> U32:
231 Settings.tenths(threshold(s))
232
233def interval_tenths(s: Json.Json) -> U32:
234 Settings.tenths(interval_seconds(s))
235
236def cooldown_tenths(s: Json.Json) -> U32:
237 Settings.tenths(cooldown_seconds(s))
238
239def hysteresis_tenths(s: Json.Json) -> U32:
240 Settings.tenths(hysteresis_pct(s))
241
242def models.keep2(skip: Bool, +q: String, rest: List<&2, String>) -> List<&2, String>:
243 match skip:
244 case True{}:
245 rest
246 case False{}:
247 q <> rest
248
249def models.keep(+q: String, rest: List<&2, String>) -> List<&2, String>:
250 models.keep2(String.is_empty(q), q, rest)
251
252
253def models.clean(ps: List<&2, String>) -> List<&2, String>:
254 match ps:
255 case Nil{}:
256 []
257 case Con{p, t}:
258 models.keep(String.trim(p), models.clean(t))
259
260def models.without2(same: Bool, +h: String, rest: List<&2, String>) -> List<&2, String>:
261 match same:
262 case True{}:
263 rest
264 case False{}:
265 h <> rest
266
267
268def models.without(xs: List<&2, String>, +low: String) -> List<&2, String>:
269 match xs:
270 case Nil{}:
271 []
272 case Con{+h, t}:
273 models.without2(String.eq(String.to_lower(h), low), h, models.without(t, low))
274
275def models.dedup(fuel: Nat, xs: List<&2, String>) -> List<&2, String>:
276 match fuel xs:
277 case 0n _:
278 xs
279 case 1n+p Nil{}:
280 []
281 case 1n+p Con{+h, t}:
282 h <> models.dedup(p, models.without(t, String.to_lower(h)))
283
284def models.dedup2(+xs: List<&2, String>) -> List<&2, String>:
285 models.dedup(List.length(&2, String, xs), xs)
286
287
288
289def model_names(v: String) -> List<&2, String>:
290 models.dedup2(models.clean(String.split(v, ',')))
291
292def model.of(j: Json.Json) -> List<&2, String>:
293 match j:
294 case Json.JStr{s}:
295 model_names(s)
296 case _:
297 []
298
299
300def model(s: Json.Json) -> List<&2, String>:
301 model.of(autoswitch(s, "model"))
302
303
304
305def clamp_text.fin(m: Maybe<&2, Settings.Dec>, lo: String, hi: String) -> Maybe<&2, String>:
306 match m:
307 case Some{d}:
308 Some{Settings.repr(Settings.clamp(d, Settings.lit(lo), Settings.lit(hi)))}
309 case None{}:
310 None{}
311
312def clamp_text(+s: String, lo: String, hi: String) -> Maybe<&2, String>:
313 clamp_text.fin(Settings.num(s), lo, hi)
314
315
316def clamp_threshold(s: String) -> Maybe<&2, String>:
317 clamp_text(s, "50", "99.9")
318
319
320def clamp_interval(s: String) -> Maybe<&2, String>:
321 clamp_text(s, "15", "3600")
322
323def clamp_cooldown(s: String) -> Maybe<&2, String>:
324 clamp_text(s, "0", "86400")
325
326def clamp_hysteresis(s: String) -> Maybe<&2, String>:
327 clamp_text(s, "0", "50")
328
329
330def clamp_strategy.pick(ok: Bool, s: String) -> String:
331 match ok:
332 case True{}:
333 s
334 case False{}:
335 "best"
336
337def clamp_strategy(+s: String) -> String:
338 clamp_strategy.pick(has_str(["best", "consume-first"], s), s)
339