src/add/run.bend

ezx@1.10.0.0 · 90 lines · 0 definitions

1# add/run: the interpreter for `ez add`. It reads the directory it runs in,
2# HOME, ez.toml, `.gitignore` and the `.bend` files git lists into a World,
3# then loops: it asks the planner (add/relock.bend) what is still open,
4# answers each question with the lock's interpreter (`Run.answer.ups`: what
5# git prints for tags, HEAD or a ref, and a checkout at a rev with its NAR
6# hash and every file's text; `Run.answer.all`: for a hub package's name,
7# what the hub says the name names, and the package's manifest and files,
8# and for the lock that follows the add, each package and name it reads, as
9# `ez lock` answers them), and adds the answers to the World, until nothing
10# is left to ask. Then it runs the planner's plan with the lock's effect
11# runner (`Run.exec.plan`), which exits 1 with the reason when the plan
12# refuses. It decides nothing; that it reads and executes faithfully is
13# EZ-TRUST-2.
14import Base
15import snap@1.3.0.0/main.bend as R
16import ezx@1.10.0.0/src/io/file.bend as F
17import ezx@1.10.0.0/src/lock/run.bend as Run
18import ezx@1.10.0.0/src/lock/up.bend as Up
19import ezx@1.10.0.0/src/lock/world.bend as W
20import ezx@1.10.0.0/src/add/world.bend as A
21import ezx@1.10.0.0/src/ws/run.bend as WsRun
22import ezx@1.10.0.0/src/add/relock.bend as AR
23import ezx@1.10.0.0/src/ws/relock.bend as Rel
24
25# the World with more answers in it
26def more(world: AR.World, rs: List<&2, Up.Reply>) -> AR.World:
27  AR.World{A.World{args, here, home, ledger, ignore, replies, hubs}, listing, locks} = world
28  AR.World{A.World{args, here, home, ledger, ignore, List.append(&2, Up.Reply, replies, rs), hubs},
29    listing, locks}
30
31# the World with more of the hub's answers in it
32def more.hub(world: AR.World, rs: List<&2, W.Reply>) -> AR.World:
33  AR.World{A.World{args, here, home, ledger, ignore, replies, hubs}, listing, locks} = world
34  AR.World{A.World{args, here, home, ledger, ignore, replies, List.append(&2, W.Reply, hubs, rs)},
35    listing, locks}
36
37# the World with more of the lock's answers in it
38def more.lock(world: AR.World, rs: List<&2, W.Reply>) -> AR.World:
39  AR.World{a, listing, locks} = world
40  AR.World{a, listing, List.append(&2, W.Reply, locks, rs)}
41
42# the planner's step: its questions answered and the loop gone round again,
43# or its plan run
44def loop.step(st: AR.Next, +lib: String, world: AR.World, go: AR.World -> IO(Unit)) -> IO(Unit):
45  match st:
46    case AR.AskGit{asks}:
47      do IO<Unit>:
48        rs : List<&2, Up.Reply> <- Run.answer.ups(lib, asks)
49        go(more(world, rs))
50    case AR.AskHub{asks}:
51      do IO<Unit>:
52        rs : List<&2, W.Reply> <- Run.answer.all(lib, asks)
53        go(more.hub(world, rs))
54    case AR.AskLock{asks}:
55      do IO<Unit>:
56        rs : List<&2, W.Reply> <- Run.answer.all(lib, asks)
57        go(more.lock(world, rs))
58    case AR.Planned{pl}:
59      Run.exec.plan(lib, pl)
60
61# the rounds the loop may take. The add asks at most three rounds for a git
62# target and two for a name; the lock after it asks a round per level of the
63# import graph, as `ez lock` does (`Run.rounds`). This is only a guard against
64# a planner that keeps asking, and running out of it dies loudly.
65def rounds() -> Nat:
66  U32.to_nat(100000)
67
68# the loop, under fuel. Every round adds an answer, since the planner never
69# asks what the World answers already, so the fuel is only a guard.
70def loop(fuel: Nat, +lib: String, +ws: Rel.Ws, +world: AR.World) -> IO(Unit):
71  match fuel:
72    case 0n:
73      IO.die(Unit, 1, "ez: ez add kept asking questions")
74    case 1n+f:
75      loop.step(Rel.add.next(ws, world), lib, world, w => loop(f, lib, ws, w))
76
77# `ez add <target> [ref] [entry.bend] [--rename NAME]`
78def run(+target: String, +ref: String, +entry: String, +rename: String) -> IO(Unit):
79  do IO<Unit>:
80    lib : String <- Run.bend.lib()
81    +pwd : String <- R.exec(["pwd"])
82    h : Result<&1, &1, U32 & String, String> <- IO.get_env("HOME")
83    +home : String <- IO.pure(String, Run.env.text(h))
84    pinned : String <- WsRun.add.ref(home, target, ref)
85    led : Maybe<&2, String> <- F.read(A.toml())
86    ig : Maybe<&2, String> <- F.read(A.ignore.file())
87    ls : W.Listing <- Run.read.listing()
88    ws : Rel.Ws <- WsRun.relock.ws()
89    loop(rounds(), lib, ws, AR.World{A.World{A.Args{target, pinned, entry, rename},
90      String.trim(R.text(pwd)), home, led, F.text_of(ig), [], []}, ls, []})