src/doctor/world.bend
ezx@1.6.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.6.0.0/src/lock/world.bend as W
20import ezx@1.6.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{})