src/doctor/world.bend

ezx@1.5.0.0 · 138 lines · 0 definitions

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/<nv>`, as it is.
18import Base
19import ezx@1.5.0.0/src/lock/world.bend as W
20import ezx@1.5.0.0/src/ledger/manifest.bend as M
21
22# a question the planner asks
23type 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/<nv>, 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
33type 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
41type Reply is Data:
42  Reply{ask: Ask, answer: Answer}
43
44# everything `ez doctor` reads
45type 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
50def toml() -> String:
51  "ez.toml"
52
53# the lock's path, which is fixed
54def lockfile() -> String:
55  "ez.lock.toml"
56
57# what `$CC` holds, "" when it is unset
58def cc(world: World) -> String:
59  World{c, _lib, _ledger, _lock, _replies} = world
60  c
61
62# what `$BEND_LIB` holds, "" when it is unset
63def 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
68def 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
73def lock(world: World) -> Maybe<&2, String>:
74  World{_cc, _lib, _ledger, l, _replies} = world
75  l
76
77# every question answered so far
78def 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
84def 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
92def 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.
97def 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
111type 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.
117def 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
125def 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
133def 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
137def sources(world: World) -> Heard:
138  heard(replies(world), Sources{})