src/relock/relock.bend

ezx@1.9.0.0 · 83 lines · 0 definitions

1# relock: what `ez add` and `ez remove` share to leave the lock in step with
2# the ledger they write (EZ-DOC-6). Each command plans its edit as before;
3# then the plain `ez lock` planner (lock/plan.bend) is run over the World the
4# edit leaves: the ledger the edit writes to ez.toml, the `.bend` files git
5# lists, and the answers to the lock's questions. The two plans become one:
6# the edit's effects, then the lines a person is told about the lock, then the
7# lock's effects, which write ez.lock.toml last. A lock that refuses refuses
8# the whole command, with no effect at all, so neither file changes.
9#
10# A plain lock moves no pin (EZ-DOC-5), so every dependency and tool the edit
11# did not touch keeps the pin it had, and only what the edit changed is read
12# anew.
13import Base
14import ezx@1.9.0.0/src/lock/plan.bend as P
15import ezx@1.9.0.0/src/lock/world.bend as W
16
17# the ledger's path, which is fixed
18def toml() -> String:
19  "ez.toml"
20
21# what ez.toml holds once a plan has run, when it held `was` before
22def ledger.of(wrote: P.Wrote, was: Maybe<&2, String>) -> Maybe<&2, String>:
23  match wrote:
24    case P.Kept{}:
25      was
26    case P.Put{text}:
27      Some{text}
28
29# the ledger an edit leaves, when ez.toml held `was` before it
30def ledger(pl: P.Plan, was: Maybe<&2, String>) -> Maybe<&2, String>:
31  ledger.of(P.put.plan(pl, toml()), was)
32
33# the World a plain `ez lock` reads, run in `here` over a ledger, the `.bend`
34# files git lists, and the answers it was given. It asks the upgrade nothing.
35def world(+here: String, ledger: Maybe<&2, String>, listing: W.Listing, rs: List<&2, W.Reply>) -> W.World:
36  W.World{W.Args{False{}, "", here}, ledger, listing, rs, []}
37
38# how a plan ends
39def outcome(pl: P.Plan) -> P.Outcome:
40  P.Plan{_es, o} = pl
41  o
42
43# whether a plan ends in a refusal
44def refuses(pl: P.Plan) -> Bool:
45  P.Plan{_es, o} = pl
46  match o:
47    case P.Success{}:
48      False{}
49    case P.Refused{_why}:
50      True{}
51
52# the edit and the lock as one plan, once the edit is known to succeed: the
53# edit's effects, the notes, and the lock's effects, or, when the lock
54# refuses, a refusal with no effect, its reason followed by `tail`
55def join.lock(lock: P.Plan, +edit: P.Plan, notes: List<&2, P.Effect>, +tail: String) -> P.Plan:
56  P.Plan{es, outcome} = lock
57  match outcome:
58    case P.Success{}:
59      P.Plan{List.append(&2, P.Effect, P.effects(edit), List.append(&2, P.Effect, notes, es)),
60        P.Success{}}
61    case P.Refused{why}:
62      P.Plan{[], P.Refused{why ++ tail}}
63
64def join.edit(refused: Bool, +edit: P.Plan, lock: P.Plan, notes: List<&2, P.Effect>, +tail: String) -> P.Plan:
65  match refused:
66    case True{}:
67      P.Plan{[], P.Refused{P.why(outcome(edit))}}
68    case False{}:
69      join.lock(lock, edit, notes, tail)
70
71# an edit followed by the lock over what it leaves. An edit that refuses is
72# the plan, with no effect; one that succeeds is joined to the lock.
73def join(+edit: P.Plan, lock: P.Plan, notes: List<&2, P.Effect>, +tail: String) -> P.Plan:
74  join.edit(refuses(edit), edit, lock, notes, tail)
75
76# what the lock still asks, as one list: the packages, then the names. A
77# plain lock asks the upgrade nothing.
78def asks(st: P.Step) -> List<&2, W.Ask>:
79  match st:
80    case P.Asking{as, _ups, ns}:
81      List.append(&2, W.Ask, as, ns)
82    case P.Run{_pl}:
83      []