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