profswitch.bend
cswap-bend-cli@0.1.0.0 · 182 lines · 33 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/clock.bend as Clock
5import cswap-bend-cli@0.1.0.0/cmd.bend as Cmd
6import cswap-bend-cli@0.1.0.0/fs.bend as Fs
7import cswap-bend-cli@0.1.0.0/setload.bend as Setload
8import cswap-bend-cli@0.1.0.0/store/base.bend as SBase
9import cswap-bend-cli@0.1.0.0/swbase.bend as B
10import cswap-bend-cli@0.1.0.0/swlive.bend as L
11import cswap-bend-cli@0.1.0.0/switch.bend as Sw
12import cswap-bend-cli@0.1.0.0/cliswitch.bend as Cliswitch
13import cswap-bend-cli@0.1.0.0/profile.bend as P
14import cswap-bend-cli@0.1.0.0/policy/types.bend as T
15import cswap-bend-cli@0.1.0.0/policy/safe.bend as SF
16import cswap-bend-cli@0.1.0.0/policy/algo.bend as A
17import cswap-bend-cli@0.1.0.0/daemon/config.bend as Cfg
18import cswap-bend-cli@0.1.0.0/daemon/gather.bend as G
19import cswap-bend-cli@0.1.0.0/daemon/view.bend as V
20
21
22
23
24
25
26
27
28
29
30def ActM(-A: Type) -> Type:
31 Act.of(A)
32
33def ActM.pure(-A: Type, x: A) -> Act.of(A):
34 Act.pure(A, x)
35
36def ActM.bind(-A: Type, -B: Type, m: Act.of(A), f: A -> Act.of(B)) -> Act.of(B):
37 Act.bind(A, B, m, f)
38
39def snap_live(+s: G.Snap) -> L.Live:
40 match s:
41 case G.Snap{seq, live, active, st, rows, ents}:
42 live
43
44def snap_ents(+s: G.Snap) -> List<&2, G.Ent>:
45 G.snap_ents(s)
46
47def snap_rows(+s: G.Snap) -> Json.Json:
48 G.snap_rows(s)
49
50def tag(+p: P.Prof, +r: Json.Json) -> Json.Json:
51 Json.set("strategy", Json.JStr{SF.name(P.algo_base(p))}, Json.set("profile", Json.JStr{P.name_of(p)}, r))
52
53def noop(+p: P.Prof, +reason: String, +message: String, +to: Json.Json) -> Json.Json:
54 tag(p, B.noop(SF.name(P.algo_base(p)), reason, message, to, []))
55
56def pad_detail2(+empty: Bool, +d: String) -> String:
57 match empty:
58 case True{}:
59 ""
60 case False{}:
61 " (" ++ d ++ ")"
62
63def pad_detail(+d: String) -> String:
64 pad_detail2(String.is_empty(d), d)
65
66def nothing_msg(+reason: T.Reason, +detail: String) -> String:
67 "Staying put: " ++ T.reason.name(reason) ++ pad_detail(detail)
68
69def decided(+p: P.Prof, +ctx: SBase.Ctx, +opts: L.Opts, +cur: Json.Json, +d: T.Decision) -> Act.of(Json.Json):
70 match d:
71 case T.Switch{to, trig, why, h, mode, rec, bar}:
72 do ActM<Json.Json>:
73 r : Json.Json <- Sw.switch_to(ctx, Nat.show(to), False{}, opts)
74 return tag(p, r)
75 case T.NoSwitch{reason, detail}:
76 Act.pure(Json.Json, noop(p, T.reason.name(reason), nothing_msg(reason, detail), cur))
77
78def usages_for(+s: G.Snap, +a: String, +t: U32) -> List<&2, T.Usage>:
79 V.usages_of(snap_rows(s), snap_ents(s), Some{a}, t)
80
81def pcfg_for(+p: P.Prof, +raw: Json.Json) -> T.Config:
82 V.pol_cfg(P.with_prof(Cfg.from_settings(raw), p))
83
84def decision_for(+p: P.Prof, +raw: Json.Json, +s: G.Snap, +a: String, +t: U32) -> T.Decision:
85 A.rotate(P.algo_base(p), V.slot_nat_of(a), V.accounts_of(snap_ents(s)), usages_for(s, a, t), V.nat(t), pcfg_for(p, raw))
86
87def cur_ref(+s: G.Snap, +a: String) -> Json.Json:
88 B.ref(Some{a}, L.lv_email(snap_live(s)))
89
90def rotate_at(+p: P.Prof, +ctx: SBase.Ctx, +opts: L.Opts, +raw: Json.Json, +s: G.Snap, +a: String, +t: U32) -> Act.of(Json.Json):
91 decided(p, ctx, opts, cur_ref(s, a), decision_for(p, raw, s, a, t))
92
93def rotate_snap(+p: P.Prof, +ctx: SBase.Ctx, +opts: L.Opts, +raw: Json.Json, +s: G.Snap, +act: Maybe<&2, String>, +t: U32) -> Act.of(Json.Json):
94 match act:
95 case None{}:
96 Act.pure(Json.Json, noop(p, "unmanaged-account", "Active account is not managed; run cswap --add-account", B.ref(None{}, L.lv_email(snap_live(s)))))
97 case Some{a}:
98 rotate_at(p, ctx, opts, raw, s, a, t)
99
100def rotate_opt(+p: P.Prof, +ctx: SBase.Ctx, +opts: L.Opts, +raw: Json.Json, +m: Maybe<&2, G.Snap>, +t: U32) -> Act.of(Json.Json):
101 match m:
102 case None{}:
103 Act.fail(Json.Json, B.e_config(), "No accounts are managed yet")
104 case Some{s}:
105 rotate_snap(p, ctx, opts, raw, s, G.snap_active(s), t)
106
107def rotate_act(+p: P.Prof, +ctx: SBase.Ctx, +opts: L.Opts, +raw: Json.Json) -> Act.of(Json.Json):
108 do ActM<Json.Json>:
109 m : Maybe<&2, G.Snap> <- G.snapshot(ctx)
110 t : U32 <- Act.lift(U32, Clock.now_s())
111 rotate_opt(p, ctx, opts, raw, m, t)
112
113def run_profile(+p: P.Prof, +ctx: SBase.Ctx, +raw: Json.Json, +json: Bool, +opts: L.Opts) -> IO(Cmd.Out):
114 IO.bind(Fs.Res(Json.Json), Cmd.Out, rotate_act(p, ctx, opts, raw), r => IO.pure(Cmd.Out, Cliswitch.switch_out(json, r, [], "")))
115
116def with_opts(+p: P.Prof, +ctx: SBase.Ctx, +raw: Json.Json, +json: Bool) -> IO(Cmd.Out):
117 IO.bind(L.Opts, Cmd.Out, Cliswitch.switch_opts(), opts => run_profile(p, ctx, raw, json, opts))
118
119
120
121def legacy(+ctx: SBase.Ctx, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
122 Cliswitch.switch_cmd(ctx, ident, force, strategy, model, json)
123
124def has(+m: Maybe<&2, String>) -> Bool:
125 match m:
126 case Some{x}:
127 True{}
128 case None{}:
129 False{}
130
131def usage_err(+msg: String) -> IO(Cmd.Out):
132 IO.pure(Cmd.Out, Cmd.Out{2, "", "cswap: error: " ++ msg ++ "\n"})
133
134
135
136def unknown_name(+explicit: Bool, +name: String, +ctx: SBase.Ctx, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
137 match explicit:
138 case True{}:
139 IO.pure(Cmd.Out, Cmd.failed(json, "ConfigError", "unknown profile '" ++ name ++ "'; `cswap profile list` shows the profiles"))
140 case False{}:
141 legacy(ctx, ident, force, strategy, model, json)
142
143def by_name(+is_default: Bool, +p: P.Prof, +ctx: SBase.Ctx, +raw: Json.Json, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
144 match is_default:
145 case True{}:
146 legacy(ctx, ident, force, strategy, model, json)
147 case False{}:
148 with_opts(p, ctx, raw, json)
149
150def resolved(+res: P.Res, +over: Maybe<&2, String>, +ctx: SBase.Ctx, +raw: Json.Json, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
151 match res:
152 case P.RNone{}:
153 legacy(ctx, ident, force, strategy, model, json)
154 case P.RUnknown{name}:
155 unknown_name(has(over), name, ctx, ident, force, strategy, model, json)
156 case P.ROk{p}:
157 by_name(String.eq(P.name_of(p), "default"), p, ctx, raw, ident, force, strategy, model, json)
158
159
160def targeted(+ident: Maybe<&2, String>, +strategy: Maybe<&2, String>) -> Bool:
161 has(ident) || has(strategy)
162
163def loaded(+over: Maybe<&2, String>, +ctx: SBase.Ctx, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool, +raw: Json.Json) -> IO(Cmd.Out):
164 resolved(P.resolve(raw, over), over, ctx, raw, ident, force, strategy, model, json)
165
166def targeted_out(+over: Maybe<&2, String>, +ctx: SBase.Ctx, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
167 match over:
168 case Some{o}:
169 usage_err("--profile can only be used with a bare 'switch' (no target and no --strategy)")
170 case None{}:
171 legacy(ctx, ident, force, strategy, model, json)
172
173def route(+tgt: Bool, +over: Maybe<&2, String>, +ctx: SBase.Ctx, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
174 match tgt:
175 case True{}:
176 targeted_out(over, ctx, ident, force, strategy, model, json)
177 case False{}:
178 IO.bind(Json.Json, Cmd.Out, Setload.load(SBase.root_of(ctx)), raw => loaded(over, ctx, ident, force, strategy, model, json, raw))
179
180
181def switch_cmd(+ctx: SBase.Ctx, +over: Maybe<&2, String>, +ident: Maybe<&2, String>, +force: Bool, +strategy: Maybe<&2, String>, +model: Maybe<&2, String>, +json: Bool) -> IO(Cmd.Out):
182 route(targeted(ident, strategy), over, ctx, ident, force, strategy, model, json)