1 # doctor/world: what `ez doctor` reads. The C compiler `$CC` names ("" when 2 # it is unset) and `$BEND_LIB` ("" when it is unset), which the planner 3 # (doctor/plan.bend) turns into the compiler and the library it reports on; 4 # the ledger's text and the lock's text, None when there is no file; and the 5 # answers to the questions the planner asked. There is no field for the 6 # clock or the rest of the environment, because the planner reads neither. 7 # 8 # A question is a program asked for its version, the names of the packages 9 # under BEND_LIB, the project's sources, or a package's tree under BEND_LIB. 10 # The sources are every `.bend` file git tracks, with its text, which is the 11 # lock's own listing (`W.Listing`), read the way `ez lock` reads it. So doctor 12 # judges the import lines `ez lock` judges. A tree is asked about with the 13 # lock's own question (`W.Ask`) and answered only from BEND_LIB: the manifest 14 # there and the text of each file it names, or that there is none. Doctor 15 # never asks the network, so this is all it can know of a package. A name the 16 # lock records is asked about as the file bend reads its hash from, 17 # `$BEND_LIB/names/`, as it is. 18 import Base 19 import ../lock/world.bend as W 20 import ../ledger/manifest.bend as M 21 22 # a question the planner asks 23 type Ask is Data: 24 Probe{name: String} # the program asked for its version 25 Lib{dir: String} # the names under the library directory, as `ls` prints them 26 Sources{} # every `.bend` file git tracks, with its text 27 Tree{ask: W.Ask} # the lock's question about a package, put to BEND_LIB 28 Name{nv: String} # the file BEND_LIB/names/, as it is 29 30 # the answer to one: what a program printed, and whether it ran; or the 31 # sources as the lock lists them; or a package's tree as BEND_LIB holds it, 32 # or that BEND_LIB holds none 33 type Answer is Data: 34 Printed{ok: Bool, text: String} # Probe, Lib 35 Listed{listing: W.Listing} # Sources 36 Laid{manifest: String, srcs: List<&2, String>} # Tree, there 37 Away{} # Tree, not there 38 Held{got: Maybe<&2, String>} # Name: the file's text, None when it is not there 39 40 # one question, answered 41 type Reply is Data: 42 Reply{ask: Ask, answer: Answer} 43 44 # everything `ez doctor` reads 45 type World is Data: 46 World{cc: String, lib: String, ledger: Maybe<&2, String>, lock: Maybe<&2, String>, 47 replies: List<&2, Reply>} 48 49 # the ledger's path, which is fixed 50 def toml() -> String: 51 "ez.toml" 52 53 # the lock's path, which is fixed 54 def lockfile() -> String: 55 "ez.lock.toml" 56 57 # what `$CC` holds, "" when it is unset 58 def cc(world: World) -> String: 59 World{c, _lib, _ledger, _lock, _replies} = world 60 c 61 62 # what `$BEND_LIB` holds, "" when it is unset 63 def lib(world: World) -> String: 64 World{_cc, l, _ledger, _lock, _replies} = world 65 l 66 67 # the ledger's text, None when there is no ez.toml 68 def ledger(world: World) -> Maybe<&2, String>: 69 World{_cc, _lib, l, _lock, _replies} = world 70 l 71 72 # the lock's text, None when there is no ez.lock.toml 73 def lock(world: World) -> Maybe<&2, String>: 74 World{_cc, _lib, _ledger, l, _replies} = world 75 l 76 77 # every question answered so far 78 def replies(world: World) -> List<&2, Reply>: 79 World{_cc, _lib, _ledger, _lock, rs} = world 80 rs 81 82 # the ledger as read: what the file says, or, with no file, a read that did 83 # not parse and says why, as every command that reads a ledger says it 84 def read.of(got: Maybe<&2, String>) -> M.Read: 85 match got: 86 case None{}: 87 M.Bad{"none here; ez init makes one"} 88 case Some{text}: 89 M.parse(text) 90 91 # the ledger, read 92 def read(world: World) -> M.Read: 93 read.of(ledger(world)) 94 95 # a question as a key. A program's name and a directory hold no newline, so 96 # the fields are kept apart by one. 97 def key(ask: Ask) -> String: 98 match ask: 99 case Probe{name}: 100 "probe\n" ++ name 101 case Lib{dir}: 102 "lib\n" ++ dir 103 case Sources{}: 104 "sources" 105 case Tree{ask}: 106 "tree\n" ++ W.ask.hash(ask) 107 case Name{nv}: 108 "name\n" ++ nv 109 110 # an answer looked up, or none yet 111 type Heard is Data: 112 Heard{answer: Answer} 113 Open{} 114 115 # this answer when it is the question's, otherwise the rest's. The rest 116 # arrives as a thunk, so the first hit ends the scan. 117 def look.step(hit: Bool, answer: Answer, rest: Unit -> Heard) -> Heard: 118 match hit: 119 case True{}: 120 Heard{answer} 121 case False{}: 122 rest(Unit{}) 123 124 # the first answer given to a question 125 def look(rs: List<&2, Reply>, +want: String) -> Heard: 126 match rs: 127 case []: 128 Open{} 129 case Reply{ask, answer} <> t: 130 look.step(String.eq(key(ask), want), answer, _u => look(t, want)) 131 132 # the answer to a question, or none yet 133 def heard(rs: List<&2, Reply>, ask: Ask) -> Heard: 134 look(rs, key(ask)) 135 136 # the answer the sources question has had on a World, or none yet 137 def sources(world: World) -> Heard: 138 heard(replies(world), Sources{})