src/ws/doctor.bend
ezx@1.11.0.0 · 295 lines · 29 definitions
1
2
3
4
5
6
7
8
9
10
11import Base
12import ezx@1.11.0.0/src/doctor/plan.bend as DP
13import ezx@1.11.0.0/src/doctor/world.bend as DW
14import ezx@1.11.0.0/src/lock/plan.bend as P
15import ezx@1.11.0.0/src/lock/world.bend as LW
16import ezx@1.11.0.0/src/ledger/manifest.bend as M
17import ezx@1.11.0.0/src/ledger/render.bend as Rend
18import ezx@1.11.0.0/src/pkg/pkg.bend as K
19import ezx@1.11.0.0/src/pub/plan.bend as PP
20import ezx@1.11.0.0/src/ws/workspace.bend as W
21import ezx@1.11.0.0/src/ws/joint.bend as J
22import ezx@1.11.0.0/src/ws/moved.bend as Mv
23
24
25
26
27type Back is Data:
28 Alone{}
29 Back{text: String, found: List<&2, J.Found>, why: String}
30
31
32type Doctored is Data:
33 Doctored{ledger: Maybe<&2, String>, back: Back}
34
35
36
37
38def joint(jj: J.Joint, +text: String, +fs: List<&2, J.Found>) -> Doctored:
39 match jj:
40 case J.Lone{}:
41 Doctored{Some{text}, Alone{}}
42 case J.Clash{why}:
43 Doctored{Some{text}, Back{text, fs, why}}
44 case J.Joint{mm}:
45 Doctored{Some{Rend.show(mm)}, Back{text, fs, ""}}
46
47
48
49
50
51def under(+dir: String, +at: String) -> Bool:
52 String.starts_with(at, dir ++ "/")
53
54
55def under.any(dirs: List<&2, List<&2, String>>, +at: String) -> Bool:
56 match dirs:
57 case []:
58 False{}
59 case +hd <> tl:
60 rest = under.any(tl, at)
61 Bool.or(Bool.and(Bool.not(List.is_empty(&2, String, hd)), under(W.segs.text(hd), at)), rest)
62
63
64
65def own.at(dir: List<&2, String>, +dirs: List<&2, List<&2, String>>, +at: String) -> Bool:
66 match dir:
67 case []:
68 Bool.not(under.any(dirs, at))
69 case hd <> tl:
70 under(W.segs.text(hd <> tl), at)
71
72
73def own.put(hit: Bool, src: LW.Source, rest: List<&2, LW.Source>) -> List<&2, LW.Source>:
74 match hit:
75 case True{}:
76 src <> rest
77 case False{}:
78 rest
79
80
81def own.files(
82 +dir: List<&2, String>,
83 +dirs: List<&2, List<&2, String>>,
84 fs: List<&2, LW.Source>
85) -> List<&2, LW.Source>:
86 match fs:
87 case []:
88 []
89 case LW.Source{+at, text} <> tl:
90 rest = own.files(dir, dirs, tl)
91 own.put(own.at(dir, dirs, at), LW.Source{at, text}, rest)
92
93
94
95
96
97def dirs.of(xs: List<&2, J.Member>) -> List<&2, List<&2, String>>:
98 match xs:
99 case []:
100 []
101 case J.Member{dir, _mm} <> tl:
102 dir <> dirs.of(tl)
103
104
105def nv.of(named: PP.Named) -> List<&2, String>:
106 match named:
107 case PP.ByName{nv}:
108 [nv]
109 case PP.ByHash{}:
110 []
111 case PP.Unnamed{_why}:
112 []
113
114
115
116
117def nvs.of(xs: List<&2, J.Member>) -> List<&2, String>:
118 match xs:
119 case []:
120 []
121 case hd <> tl:
122 List.append(&2, String, nv.of(J.member.naming(hd)), nvs.of(tl))
123
124
125
126
127def drift(+hs: List<&2, String>, +ds: List<&2, M.Dep>, +nvs: List<&2, String>) -> List<&2, String>:
128 List.append(&2, String, DP.unrecorded(DP.missing(hs, List.append(&2, String, DP.pinned(ds), nvs))),
129 DP.unused(ds, hs))
130
131
132def at.lines(+place: String, ls: List<&2, String>) -> List<&2, String>:
133 match ls:
134 case []:
135 []
136 case hd <> tl:
137 (place ++ ": " ++ hd) <> at.lines(place, tl)
138
139
140def drift.notes(+place: String, +hs: List<&2, String>, lines: List<&2, String>) -> List<&2, DP.Note>:
141 match lines:
142 case []:
143 [DP.Note{False{}, "imports: " ++ place ++ ": " ++
144 DP.plural(List.length(&2, String, hs), "package", "packages") ++ " imported, every one named in it"}]
145 case hd <> tl:
146 DP.flag(at.lines(place, hd <> tl))
147
148
149def member.drift(
150 mb: J.Member,
151 +dirs: List<&2, List<&2, String>>,
152 +nvs: List<&2, String>,
153 +fs: List<&2, LW.Source>
154) -> List<&2, DP.Note>:
155 J.Member{+dir, mm} = mb
156 +hs = DP.imports(own.files(dir, dirs, fs))
157 drift.notes(J.ledger.at(dir), hs, drift(hs, J.deps.of(mm), nvs))
158
159
160
161
162
163def moved.line(+root: String, +now: String, +held: String) -> String:
164 "workspace: member " ++ root ++ " is " ++ now ++ " in the committed files, and the lock records " ++
165 held ++ "; run ez lock"
166
167
168
169def moved.at(found: Maybe<&2, Mv.Held>, +root: String, +now: String) -> List<&2, DP.Note>:
170 match found:
171 case None{}:
172 []
173 case Some{hd}:
174 Mv.Held{_path, _name, +held} = hd
175 Bool.pick(List<&2, DP.Note>, String.eq(now, held), [], [DP.Note{True{}, moved.line(root, now, held)}])
176
177
178def moved.walked(got: K.Walked, +locked: List<&2, Mv.Held>) -> List<&2, DP.Note>:
179 match got:
180 case K.Walked{+now, +root, _fs, _sums}:
181 moved.at(Mv.at(locked, root), root, now)
182 case K.Asks{_at}:
183 []
184 case K.Refused{_why}:
185 []
186
187
188
189
190def member.moved(mb: J.Member, +files: List<&2, K.Source>, +locked: List<&2, Mv.Held>) -> List<&2, DP.Note>:
191 J.Member{+dir, mm} = mb
192 M.Manifest{_n, ee, _b, _h, _pa, _pv, _ds, _ts} = mm
193 moved.walked(K.of(J.entry.at(dir, ee), files), locked)
194
195
196def members.notes(
197 xs: List<&2, J.Member>,
198 +dirs: List<&2, List<&2, String>>,
199 +nvs: List<&2, String>,
200 +fs: List<&2, LW.Source>,
201 +locked: List<&2, Mv.Held>
202) -> List<&2, DP.Note>:
203 match xs:
204 case []:
205 []
206 case +hd <> tl:
207 rest = members.notes(tl, dirs, nvs, fs, locked)
208 List.append(&2, DP.Note, member.drift(hd, dirs, nvs, fs),
209 List.append(&2, DP.Note, member.moved(hd, J.files.of(fs), locked), rest))
210
211
212def resolved.notes(rd: J.Read, +fs: List<&2, LW.Source>, +locked: List<&2, Mv.Held>) -> List<&2, DP.Note>:
213 match rd:
214 case J.Unread{why}:
215 [DP.Note{True{}, "workspace: " ++ why}]
216 case J.Read{+xs}:
217 members.notes(xs, dirs.of(xs), nvs.of(xs), fs, locked)
218
219
220def listing.notes(
221 listing: LW.Listing,
222 +text: String,
223 +found: List<&2, J.Found>,
224 +lock: String
225) -> List<&2, DP.Note>:
226 match listing:
227 case LW.Listed{+fs}:
228 resolved.notes(J.resolved(text, found, J.files.of(fs)), fs, Mv.locked(lock))
229 case LW.Unlisted{_why}:
230 [DP.Note{True{}, "imports: git could not list the .bend files it tracks here"}]
231
232
233def sources.notes(got: DW.Heard, +text: String, +found: List<&2, J.Found>, +lock: String) -> List<&2, DP.Note>:
234 match got:
235 case DW.Heard{a}:
236 match a:
237 case DW.Listed{l}:
238 listing.notes(l, text, found, lock)
239 case _:
240 [DP.Note{True{}, "imports: the sources were not read"}]
241 case DW.Open{}:
242 [DP.Note{True{}, "imports: the sources were not read"}]
243
244
245
246
247
248
249
250def tail.pick(
251 fine: Bool,
252 +why: String,
253 +text: String,
254 +found: List<&2, J.Found>,
255 +world: DW.World
256) -> List<&2, DP.Note>:
257 match fine:
258 case True{}:
259 sources.notes(DW.heard(DW.replies(world), DW.Sources{}), text, found, DP.lock.text(DW.lock(world)))
260 case False{}:
261 [DP.Note{True{}, why}]
262
263
264def tail(+why: String, +text: String, +found: List<&2, J.Found>, +world: DW.World) -> List<&2, DP.Note>:
265 tail.pick(String.is_empty(why), why, text, found, world)
266
267
268def notes(+why: String, +text: String, +found: List<&2, J.Found>, +world: DW.World) -> List<&2, DP.Note>:
269 List.append(&2, DP.Note,
270 DP.head.notes(DP.ledger.seen(DW.read(world)), DW.cc(world), DW.lib(world), DW.lock(world),
271 DW.replies(world), DP.checked(world)),
272 tail(why, text, found, world))
273
274
275
276
277def plan(back: Back, +world: DW.World) -> P.Plan:
278 match back:
279 case Alone{}:
280 DP.plan(world)
281 case Back{+text, +found, +why}:
282 DP.plan.notes(notes(why, text, found, world))
283
284
285def step.of(st: DP.Step, back: Back, +world: DW.World) -> DP.Step:
286 match st:
287 case DP.Asking{asks}:
288 DP.Asking{asks}
289 case DP.Run{_pl}:
290 DP.Run{plan(back, world)}
291
292
293
294def step(back: Back, +world: DW.World) -> DP.Step:
295 step.of(DP.step(world), back, world)