daemon/apply.bend
cswap-bend-cli@0.1.0.0 · 135 lines · 23 definitions
1import Base
2import cswap-bend-cli@0.1.0.0/json.bend as Json
3import cswap-bend-cli@0.1.0.0/act.bend as Act
4import cswap-bend-cli@0.1.0.0/fs.bend as Fs
5import cswap-bend-cli@0.1.0.0/clock.bend as Clock
6import cswap-bend-cli@0.1.0.0/store.bend as Store
7import cswap-bend-cli@0.1.0.0/store/base.bend as SBase
8import cswap-bend-cli@0.1.0.0/store/seq.bend as SSeq
9import cswap-bend-cli@0.1.0.0/swlive.bend as L
10import cswap-bend-cli@0.1.0.0/switch.bend as Sw
11import cswap-bend-cli@0.1.0.0/swbase.bend as SwB
12import cswap-bend-cli@0.1.0.0/daemon/dec.bend as Dc
13import cswap-bend-cli@0.1.0.0/daemon/events.bend as Ev
14import cswap-bend-cli@0.1.0.0/daemon/cadence.bend as Cad
15import cswap-bend-cli@0.1.0.0/daemon/state.bend as St
16import cswap-bend-cli@0.1.0.0/daemon/config.bend as Cfg
17
18
19
20
21
22
23
24
25
26def ActM(-A: Type) -> Type:
27 Act.of(A)
28
29def ActM.pure(-A: Type, x: A) -> Act.of(A):
30 Act.pure(A, x)
31
32def ActM.bind(-A: Type, -B: Type, m: Act.of(A), f: A -> Act.of(B)) -> Act.of(B):
33 Act.bind(A, B, m, f)
34
35def warnings_of(+w: Json.Json) -> List<&2, String>:
36 SwB.texts_of(w)
37
38def switched_res(+c: Dc.Ctx, +trigger: String, +r: Json.Json) -> Dc.TickRes:
39 Dc.one(Dc.info(Cad.Switched{}, None{}, False{}, False{}, Dc.ctx_np(c)), Ev.switch(Dc.ctx_ts(c), trigger, SSeq.get_or("from", r, Json.JNull{}), SSeq.get_or("to", r, Json.JNull{}), warnings_of(SSeq.get_or("warnings", r, Json.JNull{})), False{}))
40
41def num_of(+m: Maybe<&2, Json.Json>) -> Maybe<&2, U32>:
42 match m:
43 case Some{j}:
44 Json.as_u32(j)
45 case None{}:
46 None{}
47
48def from_number(+r: Json.Json) -> Maybe<&2, U32>:
49 num_of(Json.get("number", SSeq.get_or("from", r, Json.JNull{})))
50
51def record_sw(+c: Dc.Ctx, +root: String, +to: String, +trigger: String, +lh: Maybe<&2, U32>, +lr: Maybe<&2, U32>, +r: Json.Json) -> Act.of(Dc.TickRes):
52 do ActM<Dc.TickRes>:
53 st : Json.Json <- Act.lift(Json.Json, St.read_state(root))
54 St.write_state(root, St.record_switch(st, Dc.ctx_now(c), to, from_number(r), lh, lr, trigger))
55 return switched_res(c, trigger, r)
56
57def after_switch2(+switched: Bool, +c: Dc.Ctx, +root: String, +to: String, +trigger: String, +lh: Maybe<&2, U32>, +lr: Maybe<&2, U32>, +r: Json.Json) -> Act.of(Dc.TickRes):
58 match switched:
59 case False{}:
60 Act.pure(Dc.TickRes, Dc.none_res(c, "already-active", SSeq.field_str(r, "reason")))
61 case True{}:
62 record_sw(c, root, to, trigger, lh, lr, r)
63
64def bool_of(+m: Maybe<&2, Json.Json>) -> Bool:
65 match m:
66 case Some{Json.JBool{b}}:
67 b
68 case _:
69 False{}
70
71def is_switched(+r: Json.Json) -> Bool:
72 bool_of(Json.get("switched", r))
73
74def after_switch(+c: Dc.Ctx, +root: String, go: Dc.Dec, +r: Json.Json) -> Act.of(Dc.TickRes):
75 match go:
76 case Dc.DGo{to, email, trigger, lh, lr}:
77 after_switch2(is_switched(r), c, root, to, trigger, lh, lr, r)
78 case _:
79 Act.pure(Dc.TickRes, Dc.error_res(c, "internal: not a switch"))
80
81def cooldown_applies(+trigger: String) -> Bool:
82 String.eq(trigger, "proactive") || String.eq(trigger, "consume-first")
83
84def cfg_cooldown(+cf: Cfg.Cfg) -> U32:
85 match cf:
86 case Cfg.Cfg{th, iv, cd, hy, st, ia, ut, ms}:
87 cd
88
89def run_switch(+c: Dc.Ctx, +root: String, go: Dc.Dec, +to: String, +trigger: String) -> Act.of(Dc.TickRes):
90 do ActM<Dc.TickRes>:
91 r : Json.Json <- Sw.switch_to(Dc.ctx_store(c), to, False{}, Dc.ctx_opts(c))
92 after_switch(c, root, go, r)
93
94def locked_switch2(+cool: Bool, +c: Dc.Ctx, +root: String, go: Dc.Dec, +to: String, +trigger: String) -> Act.of(Dc.TickRes):
95 Act.cond(Dc.TickRes, cool, Act.pure(Dc.TickRes, Dc.none_res(c, "cooldown", "")), run_switch(c, root, go, to, trigger))
96
97def in_cool(+c: Dc.Ctx, +st: Json.Json, +trigger: String) -> Bool:
98 cooldown_applies(trigger) && St.in_cooldown(st, Dc.ctx_now(c), cfg_cooldown(Dc.ctx_cfg(c)))
99
100def locked_switch(+c: Dc.Ctx, +root: String, go: Dc.Dec, +to: String, +trigger: String) -> Act.of(Dc.TickRes):
101 do ActM<Dc.TickRes>:
102 st : Json.Json <- Act.lift(Json.Json, St.read_state(root))
103 locked_switch2(in_cool(c, st, trigger), c, root, go, to, trigger)
104
105def dry_res(+c: Dc.Ctx, +trigger: String, +to: String, +email: String) -> Dc.TickRes:
106 Dc.one(Dc.info(Cad.Switched{}, None{}, False{}, False{}, Dc.ctx_np(c)), Ev.switch(Dc.ctx_ts(c), trigger, Dc.ctx_active(c), Ev.ref(to, email), [], True{}))
107
108def go_res(+c: Dc.Ctx, go: Dc.Dec) -> Act.of(Dc.TickRes):
109 match go:
110 case Dc.DGo{+to, +email, +trigger, lh, lr}:
111 Act.cond(Dc.TickRes, Dc.ctx_dry(c), Act.pure(Dc.TickRes, dry_res(c, trigger, to, email)),
112 St.with_state_lock(Dc.TickRes, SBase.root_of(Dc.ctx_store(c)), locked_switch(c, SBase.root_of(Dc.ctx_store(c)), go, to, trigger)))
113 case _:
114 Act.pure(Dc.TickRes, Dc.error_res(c, "internal: not a switch"))
115
116
117def recover(+c: Dc.Ctx, r: Fs.Res(Dc.TickRes)) -> Act.of(Dc.TickRes):
118 match r:
119 case Done{t}:
120 Act.pure(Dc.TickRes, t)
121 case Fail{(code, msg)}:
122 Act.pure(Dc.TickRes, Dc.error_res(c, msg))
123
124def go_safe(+c: Dc.Ctx, go: Dc.Dec) -> Act.of(Dc.TickRes):
125 IO.bind(Fs.Res(Dc.TickRes), Fs.Res(Dc.TickRes), go_res(c, go), r => recover(c, r))
126
127
128def apply(+c: Dc.Ctx, d: Dc.Dec) -> Act.of(Dc.TickRes):
129 match d:
130 case Dc.DNone{reason, detail}:
131 Act.pure(Dc.TickRes, Dc.none_res(c, reason, detail))
132 case Dc.DExhausted{earliest}:
133 Act.pure(Dc.TickRes, Dc.exhausted_res(c, earliest))
134 case Dc.DGo{to, email, trigger, lh, lr}:
135 go_safe(c, d)