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. 13 import Base 14 import ../lock/plan.bend as P 15 import ../lock/world.bend as W 16 17 # the ledger's path, which is fixed 18 def toml() -> String: 19 "ez.toml" 20 21 # what ez.toml holds once a plan has run, when it held `was` before 22 def 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 30 def 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. 35 def 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 39 def outcome(pl: P.Plan) -> P.Outcome: 40 P.Plan{_es, o} = pl 41 o 42 43 # whether a plan ends in a refusal 44 def 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` 55 def 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 64 def 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. 73 def 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. 78 def 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 []