usage/norm.bend
cswap-bend-cli@0.1.0.0 · 209 lines · 34 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/clock.bend as Clock
4import cswap-bend-cli@0.1.0.0/usage/text.bend as UText
5
6
7
8def months() -> String:
9 "JanFebMarAprMayJunJulAugSepOctNovDec"
10
11def hhmm(+local: U32) -> String:
12 Clock.pad2(((local % 86400) / 3600 : U32)) ++ ":" ++ Clock.pad2(((local % 3600) / 60 : U32))
13
14def clock.date2(md: U32 & U32, +local: U32) -> String:
15 (m, d) = md
16 String.take(String.drop(months(), U32.to_nat((m * 3 - 3 : U32))), 3n) ++ " " ++ U32.show(d) ++ " " ++ hhmm(local)
17
18def clock.date(ymd: U32 & (U32 & U32), +local: U32) -> String:
19 (y, md) = ymd
20 clock.date2(md, local)
21
22
23
24def clock.text(+reset: U32, +now: U32, +off: U32) -> String:
25 Clock.sel.s(U32.is_eq(((reset + off - 86400) / 86400 : U32), ((now + off - 86400) / 86400 : U32)),
26 hhmm((reset + off - 86400 : U32)),
27 clock.date(Clock.civil(((reset + off - 86400) / 86400 : U32)), (reset + off - 86400 : U32)))
28
29def countdown(+rem: U32) -> String:
30 Clock.sel.s(U32.is_gt((rem / 86400 : U32), 0), U32.show((rem / 86400 : U32)) ++ "d " ++ U32.show(((rem % 86400) / 3600 : U32)) ++ "h",
31 Clock.sel.s(U32.is_gt((rem / 3600 : U32), 0), U32.show((rem / 3600 : U32)) ++ "h " ++ U32.show(((rem % 3600) / 60 : U32)) ++ "m",
32 U32.show((rem / 60 : U32)) ++ "m"))
33
34def remaining(+reset: U32, +now: U32) -> U32:
35 Clock.sel(U32.is_gt(reset, now), (reset - now : U32), 0)
36
37def nonempty(m: Maybe<&2, String>) -> Maybe<&2, String>:
38 match m:
39 case None{}:
40 None{}
41 case Some{+s}:
42 UText.maybe.none_if(String.is_empty(s), s)
43
44def resets_of(w: Json.Json) -> Maybe<&2, String>:
45 nonempty(Json.as_string(Maybe.default(&2, Json.Json, Json.get("resets_at", w), Json.JNull{})))
46
47
48def with_reset.at(j: Json.Json, s: String, t: Maybe<&2, U32>, +now: U32, +off: U32) -> Json.Json:
49 match t:
50 case None{}:
51 Json.set("resets_at", Json.JStr{s}, j)
52 case Some{+u}:
53 Json.set("clock", Json.JStr{clock.text(u, now, off)},
54 Json.set("countdown", Json.JStr{countdown(remaining(u, now))},
55 Json.set("resets_at", Json.JStr{s}, j)))
56
57def with_reset(j: Json.Json, r: Maybe<&2, String>, +now: U32, +off: U32) -> Json.Json:
58 match r:
59 case None{}:
60 j
61 case Some{+s}:
62 with_reset.at(j, s, Clock.parse_iso8601(s), now, off)
63
64def put(k: String, m: Maybe<&2, Json.Json>, j: Json.Json) -> Json.Json:
65 match m:
66 case None{}:
67 j
68 case Some{v}:
69 Json.set(k, v, j)
70
71
72
73def window.pct(u: Maybe<&2, Json.Json>, r: Maybe<&2, String>, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
74 match u:
75 case None{}:
76 None{}
77 case Some{p}:
78 Some{with_reset(Json.of_pairs([("pct", p)]), r, now, off)}
79
80def window.of(+w: Json.Json, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
81 window.pct(Json.get("utilization", w), resets_of(w), now, off)
82
83def window.in(m: Maybe<&2, Json.Json>, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
84 match m:
85 case None{}:
86 None{}
87 case Some{w}:
88 window.of(w, now, off)
89
90def window(+key: String, +data: Json.Json, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
91 window.in(Json.get(key, data), now, off)
92
93def num.text(j: Json.Json) -> Maybe<&2, String>:
94 match j:
95 case Json.JNum{t}:
96 Some{t}
97 case _:
98 None{}
99
100def nonnull(m: Maybe<&2, Json.Json>) -> Maybe<&2, Json.Json>:
101 match m:
102 case None{}:
103 None{}
104 case Some{Json.JNull{}}:
105 None{}
106 case Some{j}:
107 Some{j}
108
109def num.field(+k: String, +j: Json.Json) -> Maybe<&2, String>:
110 do Maybe<&2, String>:
111 v : Json.Json <- nonnull(Json.get(k, j))
112 num.text(v)
113
114
115def float.field(+k: String, +j: Json.Json) -> Maybe<&2, String>:
116 do Maybe<&2, String>:
117 t : String <- num.field(k, j)
118 UText.float(t)
119
120def spend.mk(used: String, limit: String, pct: String, cur: Json.Json, eu: Json.Json, +now: U32, +off: U32) -> Json.Json:
121 with_reset(Json.of_pairs([("used", Json.JNum{used}), ("limit", Json.JNum{limit}), ("pct", Json.JNum{pct}), ("currency", cur)]),
122 resets_of(eu), now, off)
123
124def spend.fields(+eu: Json.Json, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
125 do Maybe<&2, Json.Json>:
126 u : String <- num.field("used_credits", eu)
127 used : String <- UText.div100(u)
128 l : String <- num.field("monthly_limit", eu)
129 limit : String <- UText.div100(l)
130 p : String <- num.field("utilization", eu)
131 pct : String <- UText.float(p)
132 return spend.mk(used, limit, pct, Maybe.default(&2, Json.Json, Json.get("currency", eu), Json.JStr{"USD"}), eu, now, off)
133
134def spend.on(enabled: Maybe<&2, Bool>, res: Json.Json, +eu: Json.Json, +now: U32, +off: U32) -> Json.Json:
135 match enabled:
136 case Some{True{}}:
137 put("spend", spend.fields(eu, now, off), res)
138 case _:
139 res
140
141def spend.add(res: Json.Json, m: Maybe<&2, Json.Json>, +now: U32, +off: U32) -> Json.Json:
142 match m:
143 case None{}:
144 res
145 case Some{+eu}:
146 spend.on(Json.as_bool(Maybe.default(&2, Json.Json, Json.get("is_enabled", eu), Json.JNull{})), res, eu, now, off)
147
148
149def scoped.entry(name: String, pct: String, +lim: Json.Json, +now: U32, +off: U32) -> Json.Json:
150 with_reset(Json.of_pairs([("name", Json.JStr{name}), ("pct", Json.JNum{pct})]), resets_of(lim), now, off)
151
152def scoped.name(+lim: Json.Json) -> Maybe<&2, String>:
153 do Maybe<&2, String>:
154 scope : Json.Json <- Json.get("scope", lim)
155 model : Json.Json <- Json.get("model", scope)
156 n : Json.Json <- Json.get("display_name", model)
157 s : String <- Json.as_string(n)
158 nonempty(Some{s})
159
160def scoped.of(name: Maybe<&2, String>, pct: Maybe<&2, String>, +lim: Json.Json, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
161 match name pct:
162 case Some{n} Some{p}:
163 Some{scoped.entry(n, p, lim, now, off)}
164 case _ _:
165 None{}
166
167def scoped.one(+lim: Json.Json, +now: U32, +off: U32) -> Maybe<&2, Json.Json>:
168 scoped.of(scoped.name(lim), float.field("percent", lim), lim, now, off)
169
170def scoped.cons(m: Maybe<&2, Json.Json>, rest: List<&2, Json.Json>) -> List<&2, Json.Json>:
171 match m:
172 case None{}:
173 rest
174 case Some{x}:
175 x <> rest
176
177def scoped.go(ls: List<&2, Json.Json>, +now: U32, +off: U32) -> List<&2, Json.Json>:
178 match ls:
179 case Nil{}:
180 []
181 case Con{h, t}:
182 scoped.cons(scoped.one(h, now, off), scoped.go(t, now, off))
183
184def scoped.put(xs: List<&2, Json.Json>, res: Json.Json) -> Json.Json:
185 match xs:
186 case Nil{}:
187 res
188 case Con{h, t}:
189 Json.set("scoped", Json.arr(h <> t), res)
190
191def result.fin(ks: List<&2, String>, res: Json.Json) -> Json.Json:
192 match ks:
193 case Nil{}:
194 Json.JNull{}
195 case Con{h, t}:
196 res
197
198def result.keep(+r: Json.Json) -> Json.Json:
199 result.fin(Json.keys(r), r)
200
201
202
203
204def build_usage_result(+data: Json.Json, +now: U32, +off: U32) -> Json.Json:
205 result.keep(scoped.put(
206 scoped.go(Json.to_list(Maybe.default(&2, Json.Json, Json.get("limits", data), Json.JNull{})), now, off),
207 spend.add(
208 put("seven_day", window("seven_day", data, now, off), put("five_hour", window("five_hour", data, now, off), Json.of_pairs([]))),
209 Json.get("extra_usage", data), now, off)))