1 # ez/start: what `ez check`, `ez build` and `ez run` start, decided from the 2 # ledger they read and the words they were given. Each starts one bend on 3 # the entry the ledger names, or refuses and starts nothing when there is no 4 # ez.toml or it does not parse, as cargo refuses a directory with no 5 # Cargo.toml. The interpreter (`ez/cmd.bend`) reads ez.toml, puts the 6 # project's BEND_LIB in front of the line (`Env.line`), runs it and exits; 7 # that it does so faithfully is EZ-TRUST-2. 8 import Base 9 import ../ledger/manifest.bend as M 10 import ../share/args.bend as Args 11 12 # which of the three commands is asking, with what it was told: where a 13 # build goes, and every word of the line for a run 14 type Ask is Data: 15 Check{} 16 Build{out: String} 17 Run{as: List<&2, String>} 18 19 # what a command does: refuse with a reason, having started nothing, or start 20 # bend with this line 21 type Start is Data: 22 Refused{why: String} 23 Starts{line: List<&2, String>} 24 25 # what each command is for, in the words a refusal says it with 26 def does(ask: Ask) -> String: 27 match ask: 28 case Check{}: 29 "ez check checks a project's entry" 30 case Build{_out}: 31 "ez build builds a project's entry" 32 case Run{_as}: 33 "ez run runs a project's entry" 34 35 # why a directory with no ez.toml is refused 36 def none(ask: Ask) -> String: 37 "ez: no ez.toml here; " ++ does(ask) ++ ", and ez init makes one" 38 39 # the entry file a ledger names, or "main.bend" when it names none 40 def entry.or(+file: String) -> String: 41 Bool.pick(String, String.is_empty(file), "main.bend", file) 42 43 # the entry file of a ledger that parsed 44 def entry(ledger: M.Manifest) -> String: 45 M.Manifest{_n, e, _b, _h, _pa, _pv, _ds, _ts} = ledger 46 entry.or(e) 47 48 # the name a ledger that parsed gives the package 49 def named(ledger: M.Manifest) -> String: 50 M.Manifest{n, _e, _b, _h, _pa, _pv, _ds, _ts} = ledger 51 n 52 53 # where a build lands when nothing said: `bin/.out`. The name 54 # and not the entry file, because the entry is where a program starts and the 55 # name is what it is called: a package named `bolt` with its entry at 56 # `bolt/main.bend` builds `bin/bolt.out` and not `bin/main.out`. 57 def build.named(+name: String) -> String: 58 "bin/" ++ name ++ ".out" 59 60 # where a build lands: what was asked for, or the default 61 def build.out(+at: String, +name: String) -> String: 62 Bool.pick(String, String.is_empty(at), build.named(name), at) 63 64 # the line each command starts bend with, over a ledger that parsed: the 65 # entry checked into `.ez/check.js` and never run, built to a native binary, 66 # or run with every word after `run` (EZ-TOOL-9) 67 def line(ask: Ask, +ledger: M.Manifest) -> List<&2, String>: 68 match ask: 69 case Check{}: 70 ["bend", entry(ledger), "-o", ".ez/check.js"] 71 case Build{+out}: 72 ["bend", entry(ledger), "-o", build.out(out, named(ledger))] 73 case Run{as}: 74 Args.run.line(entry(ledger), as) 75 76 # what a command does with the ledger it read 77 def read(+ask: Ask, ledger: M.Read) -> Start: 78 match ledger: 79 case M.Bad{why}: 80 Refused{"ez: " ++ M.show(M.Bad{why})} 81 case M.Good{+m}: 82 Starts{line(ask, m)} 83 84 # what a command does with ez.toml's text, None when there is none 85 def start(+ask: Ask, text: Maybe<&2, String>) -> Start: 86 match text: 87 case None{}: 88 Refused{none(ask)} 89 case Some{t}: 90 read(ask, M.parse(t)) 91 92 # the status `ez run` exits with, given the status of the program it started 93 # (EZ-OUT-1): 1 for a refusal, which started none, and the program's own 94 # status otherwise, as `cargo run` exits. `ez check` and `ez build` end on 95 # what bend printed instead (`ez/ends.bend`). 96 def code(start: Start, program: U32) -> U32: 97 match start: 98 case Refused{_why}: 99 1 100 case Starts{_line}: 101 program 102 103 # why a command refused, or "" when it started bend 104 def why(start: Start) -> String: 105 match start: 106 case Refused{w}: 107 w 108 case Starts{_line}: 109 ""