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. 14 import Base 15 import 0xead4c7939c9eaca8d47da53b4496b731/main.bend as R 16 import ../io/file.bend as F 17 import ../lock/run.bend as Run 18 import ../lock/up.bend as Up 19 import ../lock/world.bend as W 20 import ./world.bend as A 21 import ./relock.bend as AR 22 23 # the World with more answers in it 24 def more(world: AR.World, rs: List<&2, Up.Reply>) -> AR.World: 25 AR.World{A.World{args, here, home, ledger, ignore, replies, hubs}, listing, locks} = world 26 AR.World{A.World{args, here, home, ledger, ignore, List.append(&2, Up.Reply, replies, rs), hubs}, 27 listing, locks} 28 29 # the World with more of the hub's answers in it 30 def more.hub(world: AR.World, rs: List<&2, W.Reply>) -> AR.World: 31 AR.World{A.World{args, here, home, ledger, ignore, replies, hubs}, listing, locks} = world 32 AR.World{A.World{args, here, home, ledger, ignore, replies, List.append(&2, W.Reply, hubs, rs)}, 33 listing, locks} 34 35 # the World with more of the lock's answers in it 36 def more.lock(world: AR.World, rs: List<&2, W.Reply>) -> AR.World: 37 AR.World{a, listing, locks} = world 38 AR.World{a, listing, List.append(&2, W.Reply, locks, rs)} 39 40 # the planner's step: its questions answered and the loop gone round again, 41 # or its plan run 42 def loop.step(st: AR.Next, +lib: String, world: AR.World, go: AR.World -> IO(Unit)) -> IO(Unit): 43 match st: 44 case AR.AskGit{asks}: 45 do IO: 46 rs : List<&2, Up.Reply> <- Run.answer.ups(lib, asks) 47 go(more(world, rs)) 48 case AR.AskHub{asks}: 49 do IO: 50 rs : List<&2, W.Reply> <- Run.answer.all(lib, asks) 51 go(more.hub(world, rs)) 52 case AR.AskLock{asks}: 53 do IO: 54 rs : List<&2, W.Reply> <- Run.answer.all(lib, asks) 55 go(more.lock(world, rs)) 56 case AR.Planned{pl}: 57 Run.exec.plan(lib, pl) 58 59 # the rounds the loop may take. The add asks at most three rounds for a git 60 # target and two for a name; the lock after it asks a round per level of the 61 # import graph, as `ez lock` does (`Run.rounds`). This is only a guard against 62 # a planner that keeps asking, and running out of it dies loudly. 63 def rounds() -> Nat: 64 U32.to_nat(100000) 65 66 # the loop, under fuel. Every round adds an answer, since the planner never 67 # asks what the World answers already, so the fuel is only a guard. 68 def loop(fuel: Nat, +lib: String, +world: AR.World) -> IO(Unit): 69 match fuel: 70 case 0n: 71 IO.die(Unit, 1, "ez: ez add kept asking questions") 72 case 1n+f: 73 loop.step(AR.next(world), lib, world, w => loop(f, lib, w)) 74 75 # `ez add [ref] [entry.bend] [--rename NAME]` 76 def run(+target: String, +ref: String, +entry: String, +rename: String) -> IO(Unit): 77 do IO: 78 lib : String <- Run.bend.lib() 79 +pwd : String <- R.exec(["pwd"]) 80 h : Result<&1, &1, U32 & String, String> <- IO.get_env("HOME") 81 led : Maybe<&2, String> <- F.read(A.toml()) 82 ig : Maybe<&2, String> <- F.read(A.ignore.file()) 83 ls : W.Listing <- Run.read.listing() 84 loop(rounds(), lib, AR.World{A.World{A.Args{target, ref, entry, rename}, 85 String.trim(R.text(pwd)), Run.env.text(h), led, F.text_of(ig), [], []}, ls, []})