src/lock/run.bend
ezx@1.11.0.0 · 754 lines · 74 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import Base
16import snap@1.4.0.0/main.bend as R
17import ezx@1.11.0.0/src/io/file.bend as F
18import ezx@1.11.0.0/src/hub/hub.bend as Web
19import ezx@1.11.0.0/src/hub/get.bend as Net
20import ezx@1.11.0.0/src/git/git.bend as Git
21import ezx@1.11.0.0/src/git/exec.bend as GX
22import ezx@1.11.0.0/src/pkg/pkg.bend as K
23import ezx@1.11.0.0/src/pkg/path.bend as P
24import ezx@1.11.0.0/src/share/say.bend as Say
25import ezx@1.11.0.0/src/share/spin.bend as Spin
26import ezx@1.11.0.0/src/lock/lock.bend as L
27import ezx@1.11.0.0/src/lock/world.bend as W
28import ezx@1.11.0.0/src/lock/up.bend as Up
29import ezx@1.11.0.0/src/lock/plan.bend as Plan
30import ezx@1.11.0.0/src/ws/run.bend as WsRun
31import ezx@1.11.0.0/src/ws/up.bend as WsUp
32import ezx@1.11.0.0/src/ws/joint.bend as J
33
34
35
36
37
38def env.text(res: Result<&1, &1, U32 & String, String>) -> String:
39 match res:
40 case Fail{_e}:
41 ""
42 case Done{s}:
43 s
44
45
46def lib.or(+dir: String) -> String:
47 Bool.pick(String, String.is_empty(dir), ".ez/lib", dir)
48
49
50def bend.lib() -> IO(String):
51 do IO<String>:
52 r : Result<&1, &1, U32 & String, String> <- IO.get_env("BEND_LIB")
53 return lib.or(String.trim(env.text(r)))
54
55
56def nonempty(ss: List<&2, String>) -> List<&2, String>:
57 match ss:
58 case []:
59 []
60 case +h <> t:
61 +more = nonempty(t)
62 Bool.pick(List<&2, String>, String.is_empty(h), more, h <> more)
63
64
65def read.ledger() -> IO(Maybe<&2, String>):
66 F.read("ez.toml")
67
68
69def read.all(ps: List<&2, String>) -> IO(List<&2, W.Source>):
70 match ps:
71 case []:
72 IO.pure(List<&2, W.Source>, [])
73 case +h <> t:
74 do IO<List<&2, W.Source>>:
75 m : Maybe<&2, String> <- F.read(h)
76 rest : List<&2, W.Source> <- read.all(t)
77 return W.Source{h, F.text_of(m)} <> rest
78
79
80def listed(ok: Bool, +text: String) -> IO(W.Listing):
81 match ok:
82 case True{}:
83 do IO<W.Listing>:
84 fs : List<&2, W.Source> <- read.all(nonempty(String.lines(text)))
85 return W.Listed{fs}
86 case False{}:
87 IO.pure(W.Listing, W.Unlisted{
88 "ez: ez lock reads the .bend files git tracks, and git could not list them: " ++
89 String.trim(text)})
90
91
92
93def read.listing() -> IO(W.Listing):
94 do IO<W.Listing>:
95 +out : String <- R.exec(["git", "-c", "core.quotepath=off", "ls-files",
96 "--", "*.bend"])
97 listed(R.ok(out), R.text(out))
98
99
100
101
102
103def gots(+how: W.How, +manifest: String, gs: List<&2, Web.Got>, acc: List<&2, String>) -> W.Answer:
104 match gs:
105 case []:
106 W.Got{how, manifest, List.reverse(&2, String, acc)}
107 case g <> t:
108 match g:
109 case Web.Miss{why}:
110 W.Miss{"ez: " ++ why}
111 case Web.Have{body}:
112 gots(how, manifest, t, body <> acc)
113
114
115def hub.all(+hub: String, +hash: String, fs: List<&2, K.Item>) -> IO(List<&2, Web.Got>):
116 match fs:
117 case []:
118 IO.pure(List<&2, Web.Got>, [])
119 case +h <> t:
120 do IO<List<&2, Web.Got>>:
121 g : Web.Got <- Net.fetch(L.url_of(hub, hash, K.file.at(h)), K.file.sum(h))
122 rest : List<&2, Web.Got> <- hub.all(hub, hash, t)
123 return g <> rest
124
125
126def hub.got(got: Web.Got, +hub: String, +hash: String) -> IO(W.Answer):
127 match got:
128 case Web.Miss{why}:
129 IO.pure(W.Answer, W.Miss{why})
130 case Web.Have{+body}:
131 do IO<W.Answer>:
132 gs : List<&2, Web.Got> <- hub.all(hub, hash, L.manifest.files(String.lines(body)))
133 return gots(W.Served{}, body, gs, [])
134
135
136def answer.hub(+hub: String, +hash: String) -> IO(W.Answer):
137 do IO<W.Answer>:
138 +s : Say.Spin <- Spin.hold(Say.dep.hub(hash))
139 g : Web.Got <- Net.fetch(L.url_of(hub, hash, "manifest"), L.want(hash))
140 a : W.Answer <- hub.got(g, hub, hash)
141 Spin.done(s)
142 return a
143
144
145
146def disk.all(+dir: String, fs: List<&2, K.Item>) -> IO(List<&2, String>):
147 match fs:
148 case []:
149 IO.pure(List<&2, String>, [])
150 case +h <> t:
151 do IO<List<&2, String>>:
152 m : Maybe<&2, String> <- F.read(P.join(dir, K.file.at(h)))
153 rest : List<&2, String> <- disk.all(dir, t)
154 return F.text_of(m) <> rest
155
156
157def answer.lib(+dir: String, +manifest: String) -> IO(W.Answer):
158 do IO<W.Answer>:
159 ss : List<&2, String> <- disk.all(dir, L.manifest.files(String.lines(manifest)))
160 return W.Got{W.Lib{}, manifest, ss}
161
162
163
164def clone.miss(+work: String, +why: String) -> IO(W.Answer):
165 do IO<W.Answer>:
166 _rm : String <- R.exec(["rm", "-rf", work])
167 return W.Miss{why}
168
169
170
171def clone.ran(ok: Bool, +work: String, +why: String, go: Unit -> IO(W.Answer)) -> IO(W.Answer):
172 match ok:
173 case True{}:
174 go(Unit{})
175 case False{}:
176 clone.miss(work, why)
177
178
179def clone.run(+work: String, what: String, args: List<&2, String>, go: Unit -> IO(W.Answer)) -> IO(W.Answer):
180 do IO<W.Answer>:
181 +out : String <- R.exec(args)
182 clone.ran(R.ok(out), work, "ez: git " ++ what ++ ": " ++ R.text(out), go)
183
184
185def clone.walk(+work: String, +entry: String, +nar: String) -> IO(W.Answer):
186 do IO<W.Answer>:
187 +p : K.Pkg <- K.pkg_in(work, entry)
188 +fs : List<&2, K.Item> <- IO.pure(List<&2, K.Item>, K.pkg.files(p))
189 ss : List<&2, String> <- disk.all(K.pkg.root(p), fs)
190 _rm : String <- R.exec(["rm", "-rf", work])
191 return W.Got{W.Clone{nar}, K.manifest_of(fs), ss}
192
193
194def clone.entry(ok: Bool, +work: String, +entry: String, +url: String, +rev: String, +nar: String) -> IO(W.Answer):
195 match ok:
196 case True{}:
197 clone.walk(work, entry, nar)
198 case False{}:
199 clone.miss(work, "ez: " ++ entry ++ " is not in " ++ url ++ " at " ++ rev)
200
201
202def clone.weigh(+work: String, +url: String, +rev: String, +entry: String) -> IO(W.Answer):
203 do IO<W.Answer>:
204 _dg : String <- R.exec(["rm", "-rf", work ++ "/.git"])
205 +s : Say.Spin <- Spin.hold(Say.hashing(rev))
206 +nar : String <- GX.nar(work)
207 Spin.done(s)
208 +ck : String <- R.exec(["test", "-f", P.join(work, entry)])
209 clone.entry(R.ok(ck), work, entry, url, rev, nar)
210
211
212
213
214
215def answer.clone(+lib: String, +hash: String, +url: String, +rev: String, +entry: String) -> IO(W.Answer):
216 +work = lib ++ "/.work-" ++ hash
217 do IO<W.Answer>:
218 _rm : String <- R.exec(["rm", "-rf", work])
219 _mk : String <- R.exec(["mkdir", "-p", work])
220 +s : Say.Spin <- Spin.hold(Say.dep.git(Say.label(hash, entry), url))
221 a : W.Answer <- clone.run(work, "init", ["git", "init", "-q", work], _u =>
222 clone.run(work, "fetch", ["git", "-C", work, "fetch", "-q", "--depth", "1", url, rev], _v =>
223 clone.run(work, "checkout", ["git", "-C", work, "checkout", "-q", rev], _w =>
224 clone.weigh(work, url, rev, entry))))
225 Spin.done(s)
226 return a
227
228
229def git.seen(
230 got: Maybe<&2, String>,
231 +lib: String,
232 +hash: String,
233 +url: String,
234 +rev: String,
235 +entry: String
236) -> IO(W.Answer):
237 match got:
238 case Some{text}:
239 answer.lib(lib ++ "/" ++ hash, text)
240 case None{}:
241 answer.clone(lib, hash, url, rev, entry)
242
243
244def answer.git(+lib: String, +hash: String, +url: String, +rev: String, +entry: String) -> IO(W.Answer):
245 do IO<W.Answer>:
246 m : Maybe<&2, String> <- F.read(lib ++ "/" ++ hash ++ "/manifest")
247 git.seen(m, lib, hash, url, rev, entry)
248
249
250def answer.pkg(+lib: String, +hash: String, src: L.Src, +hub: String) -> IO(W.Answer):
251 match src:
252 case L.Hub{}:
253 answer.hub(hub, hash)
254 case L.Git{url, rev, entry, _root, _nar, _tag}:
255 answer.git(lib, hash, url, rev, entry)
256 case L.Member{path, _entry}:
257 IO.pure(W.Answer, W.Miss{"ez: the workspace member at " ++ path ++ " was not walked"})
258
259
260
261def said(got: Web.Got) -> W.Answer:
262 match got:
263 case Web.Miss{why}:
264 W.Miss{"ez: " ++ why}
265 case Web.Have{body}:
266 W.Said{body}
267
268
269def answer.name(+hub: String, +nv: String) -> IO(W.Answer):
270 do IO<W.Answer>:
271 +s : Say.Spin <- Spin.hold(Say.dep.hub(nv))
272 g : Web.Got <- Net.fetch(hub ++ "/name/" ++ nv, "")
273 Spin.done(s)
274 return said(g)
275
276
277def answer.lock() -> IO(W.Answer):
278 do IO<W.Answer>:
279 m : Maybe<&2, String> <- F.read(W.lockfile())
280 return W.Locked{F.text_of(m)}
281
282
283def answer(+lib: String, ask: W.Ask) -> IO(W.Answer):
284 match ask:
285 case W.Pkg{+hash, src, +hub}:
286 answer.pkg(lib, hash, src, hub)
287 case W.Name{+nv, +hub}:
288 answer.name(hub, nv)
289 case W.Lock{}:
290 answer.lock()
291
292
293def answer.all(+lib: String, asks: List<&2, W.Ask>) -> IO(List<&2, W.Reply>):
294 match asks:
295 case []:
296 IO.pure(List<&2, W.Reply>, [])
297 case +h <> t:
298 do IO<List<&2, W.Reply>>:
299 a : W.Answer <- answer(lib, h)
300 rest : List<&2, W.Reply> <- answer.all(lib, t)
301 return W.Reply{h, a} <> rest
302
303
304
305
306
307def git.said(+what: String, args: List<&2, String>) -> IO(Up.Answer):
308 do IO<Up.Answer>:
309 +s : Say.Spin <- Spin.hold(what)
310 +out : String <- R.exec(args)
311 Spin.done(s)
312 return Up.Printed{R.ok(out), R.text(out)}
313
314
315
316
317
318
319
320def answer.refs(+url: String, +ref: String) -> IO(Up.Answer):
321 git.said(Say.resolving.at(url), ["git", "ls-remote", url, "refs/tags/" ++ ref,
322 "refs/tags/" ++ ref ++ "^{}", "refs/heads/" ++ ref])
323
324
325def answer.head(+url: String) -> IO(Up.Answer):
326 git.said(Say.resolving.at(url), ["git", "ls-remote", "--symref", url, "HEAD"])
327
328
329def answer.tags(+url: String) -> IO(Up.Answer):
330 git.said(Say.resolving.at(url), ["git", "ls-remote", "--tags", url])
331
332
333def answer.above(+lib: String, +url: String, +pin: String, +tip: String) -> IO(Up.Answer):
334 do IO<Up.Answer>:
335 yes : Bool <- GX.descendant.at(lib ++ "/.work-above", url, pin, tip)
336 return Up.Onward{yes}
337
338
339
340def tree.walk(+work: String, +at: String, +nar: String) -> IO(Up.Answer):
341 do IO<Up.Answer>:
342 +p : K.Pkg <- K.pkg_in(work, at)
343 +fs : List<&2, K.Item> <- IO.pure(List<&2, K.Item>, K.pkg.files(p))
344 ss : List<&2, String> <- disk.all(K.pkg.root(p), fs)
345 +rt : String <- IO.pure(String, Git.root.rel(P.join(work, at), K.pkg.root(p), at))
346 _rm : String <- R.exec(["rm", "-rf", work])
347 return Up.Checkout{nar, rt, K.manifest_of(fs), ss}
348
349
350def tree.entry(ok: Bool, +work: String, +at: String, +url: String, +rev: String, +nar: String) -> IO(Up.Answer):
351 match ok:
352 case True{}:
353 tree.walk(work, at, nar)
354 case False{}:
355 do IO<Up.Answer>:
356 _rm : String <- R.exec(["rm", "-rf", work])
357 return Up.Miss{"ez: " ++ at ++ " is not in " ++ url ++ " at " ++ rev}
358
359
360
361
362
363def answer.tree(+lib: String, +url: String, +rev: String, +entry: String) -> IO(Up.Answer):
364 +work = lib ++ "/.work-" ++ rev
365 do IO<Up.Answer>:
366 _rm : String <- R.exec(["rm", "-rf", work])
367 _mk : String <- R.exec(["mkdir", "-p", work])
368 GX.clone(work, url, rev)
369 +at : String <- Git.entry.for(Bool.not(String.is_empty(entry)), entry, work)
370 _dg : String <- R.exec(["rm", "-rf", work ++ "/.git"])
371 +s : Say.Spin <- Spin.hold(Say.hashing(rev))
372 +nar : String <- GX.nar(work)
373 Spin.done(s)
374 +ck : String <- R.exec(["test", "-f", P.join(work, at)])
375 tree.entry(R.ok(ck), work, at, url, rev, nar)
376
377
378def answer.weigh(+url: String, +rev: String) -> IO(Up.Answer):
379 do IO<Up.Answer>:
380 +nar : String <- GX.weigh(url, rev)
381 return Up.Weighed{nar}
382
383
384def answer.hubhas(+hub: String, +hash: String) -> IO(Up.Answer):
385 do IO<Up.Answer>:
386 g : Web.Got <- Net.fetch(L.url_of(hub, hash, "manifest"), L.want(hash))
387 return Up.HubSaid{g}
388
389
390def answer.ignore() -> IO(Up.Answer):
391 do IO<Up.Answer>:
392 m : Maybe<&2, String> <- F.read(".gitignore")
393 return Up.Plain{F.text_of(m)}
394
395
396def found.put(got: Maybe<&2, String>, +at: String, rest: Up.Answer) -> Up.Answer:
397 match got:
398 case None{}:
399 Up.Miss{"ez: " ++ at ++ " could not be read"}
400 case Some{text}:
401 match rest:
402 case Up.Files{fs}:
403 Up.Files{Up.Found{at, text} <> fs}
404 case _:
405 rest
406
407
408def found.all(ps: List<&2, String>) -> IO(Up.Answer):
409 match ps:
410 case []:
411 IO.pure(Up.Answer, Up.Files{[]})
412 case +h <> t:
413 do IO<Up.Answer>:
414 m : Maybe<&2, String> <- F.read(h)
415 rest : Up.Answer <- found.all(t)
416 return found.put(m, h, rest)
417
418def found.listed(ok: Bool, +text: String) -> IO(Up.Answer):
419 match ok:
420 case True{}:
421 found.all(nonempty(String.lines(text)))
422 case False{}:
423 IO.pure(Up.Answer, Up.Miss{"ez: could not list sources: " ++ String.trim(text)})
424
425
426
427def answer.sources() -> IO(Up.Answer):
428 do IO<Up.Answer>:
429 +out : String <- R.exec(["find", ".", "-name", "*.bend",
430 "-not", "-path", "*/.ez/*", "-not", "-path", "*/.git/*"])
431 found.listed(R.ok(out), R.text(out))
432
433
434
435def whole.miss(+work: String, +why: String) -> IO(Up.Answer):
436 do IO<Up.Answer>:
437 _rm : String <- R.exec(["rm", "-rf", work])
438 return Up.Miss{why}
439
440
441
442def whole.ran(ok: Bool, +work: String, +why: String, go: Unit -> IO(Up.Answer)) -> IO(Up.Answer):
443 match ok:
444 case True{}:
445 go(Unit{})
446 case False{}:
447 whole.miss(work, why)
448
449
450def whole.run(+work: String, what: String, args: List<&2, String>, go: Unit -> IO(Up.Answer)) -> IO(Up.Answer):
451 do IO<Up.Answer>:
452 +out : String <- R.exec(args)
453 whole.ran(R.ok(out), work, "ez: git " ++ what ++ ": " ++ String.trim(R.text(out)), go)
454
455
456def whole.put(got: Maybe<&2, String>, +at: String, rest: List<&2, K.Source>) -> List<&2, K.Source>:
457 match got:
458 case None{}:
459 rest
460 case Some{text}:
461 K.Source{at, text} <> rest
462
463
464
465
466def whole.read(+work: String, ps: List<&2, String>) -> IO(List<&2, K.Source>):
467 match ps:
468 case []:
469 IO.pure(List<&2, K.Source>, [])
470 case +h <> t:
471 do IO<List<&2, K.Source>>:
472 m : Maybe<&2, String> <- F.read(h)
473 rest : List<&2, K.Source> <- whole.read(work, t)
474 return whole.put(m, String.drop(h, (1n + String.length(work) : Nat)), rest)
475
476
477
478
479
480
481
482def whole.listed(ok: Bool, +work: String, +nar: String, +text: String) -> IO(Up.Answer):
483 match ok:
484 case True{}:
485 do IO<Up.Answer>:
486 fs : List<&2, K.Source> <- whole.read(work, nonempty(String.lines(text)))
487 _rm : String <- R.exec(["rm", "-rf", work])
488 return Up.Cloned{nar, fs}
489 case False{}:
490 do IO<Up.Answer>:
491 _rm : String <- R.exec(["rm", "-rf", work])
492 return Up.Miss{"ez: could not list the checkout: " ++ String.trim(text)}
493
494def whole.weigh(+work: String, +rev: String) -> IO(Up.Answer):
495 do IO<Up.Answer>:
496 _dg : String <- R.exec(["rm", "-rf", work ++ "/.git"])
497 +s : Say.Spin <- Spin.hold(Say.hashing(rev))
498 +nar : String <- GX.nar(work)
499 Spin.done(s)
500 +out : String <- R.exec(["find", work, "(", "-type", "f", "-o", "-type", "l", ")"])
501 whole.listed(R.ok(out), work, nar, R.text(out))
502
503
504def whole.dir(ok: Bool, +work: String, +url: String, +rev: String) -> IO(Up.Answer):
505 match ok:
506 case False{}:
507 IO.pure(Up.Answer, Up.Miss{"ez: no scratch directory to clone into: " ++ work})
508 case True{}:
509 do IO<Up.Answer>:
510 +s : Say.Spin <- Spin.hold(Say.git.fetch(url))
511 a : Up.Answer <- whole.run(work, "init", ["git", "init", "-q", work], _u =>
512 whole.run(work, "fetch", ["git", "-C", work, "fetch", "-q", "--depth", "1", url, rev], _v =>
513 whole.run(work, "checkout", ["git", "-C", work, "checkout", "-q", rev], _w =>
514 whole.weigh(work, rev))))
515 Spin.done(s)
516 return a
517
518
519
520
521
522def answer.whole(+url: String, +rev: String) -> IO(Up.Answer):
523 do IO<Up.Answer>:
524 +tmp : String <- R.exec(["mktemp", "-d"])
525 whole.dir(R.ok(tmp), String.trim(R.text(tmp)), url, rev)
526
527
528def answer.up(+lib: String, ask: Up.Ask) -> IO(Up.Answer):
529 match ask:
530 case Up.Refs{url, ref}:
531 answer.refs(url, ref)
532 case Up.Head{url}:
533 answer.head(url)
534 case Up.Tags{url}:
535 answer.tags(url)
536 case Up.Above{url, pin, tip}:
537 answer.above(lib, url, pin, tip)
538 case Up.Tree{url, rev, entry}:
539 answer.tree(lib, url, rev, entry)
540 case Up.Weigh{url, rev}:
541 answer.weigh(url, rev)
542 case Up.HubHas{hub, hash}:
543 answer.hubhas(hub, hash)
544 case Up.Ignore{}:
545 answer.ignore()
546 case Up.Sources{}:
547 answer.sources()
548 case Up.Clone{url, rev}:
549 answer.whole(url, rev)
550
551
552def answer.ups(+lib: String, asks: List<&2, Up.Ask>) -> IO(List<&2, Up.Reply>):
553 match asks:
554 case []:
555 IO.pure(List<&2, Up.Reply>, [])
556 case +h <> t:
557 do IO<List<&2, Up.Reply>>:
558 a : Up.Answer <- answer.up(lib, h)
559 rest : List<&2, Up.Reply> <- answer.ups(lib, t)
560 return Up.Reply{h, a} <> rest
561
562
563
564
565def answer.queries(+lib: String, qs: List<&2, Plan.Query>) -> IO(List<&2, Up.Reply>):
566 match qs:
567 case []:
568 IO.pure(List<&2, Up.Reply>, [])
569 case Plan.Query{ask, git} <> t:
570 do IO<List<&2, Up.Reply>>:
571 a : Up.Answer <- answer.up(lib, git)
572 rest : List<&2, Up.Reply> <- answer.queries(lib, t)
573 return Up.Reply{ask, a} <> rest
574
575
576def more(world: W.World, rs: List<&2, W.Reply>, us: List<&2, Up.Reply>) -> W.World:
577 W.World{args, ledger, listing, replies, ups} = world
578 W.World{args, ledger, listing, List.append(&2, W.Reply, replies, rs),
579 List.append(&2, Up.Reply, ups, us)}
580
581
582
583
584
585def wrote(ok: Bool, +at: String) -> IO(Unit):
586 match ok:
587 case True{}:
588 IO.pure(Unit, Unit{})
589 case False{}:
590 IO.die(Unit, 1, "ez: " ++ at ++ " could not be written")
591
592
593def write(+at: String, text: String) -> IO(Unit):
594 do IO<Unit>:
595 _mk : String <- R.exec(["mkdir", "-p", P.dir(at)])
596 ok : Bool <- F.write(at, text)
597 wrote(ok, at)
598
599
600def lay.all(+dir: String, fs: List<&2, W.Source>) -> IO(Unit):
601 match fs:
602 case []:
603 IO.pure(Unit, Unit{})
604 case W.Source{at, text} <> t:
605 do IO<Unit>:
606 write(dir ++ "/" ++ at, text)
607 lay.all(dir, t)
608
609
610def place(+lib: String, pl: Plan.Place) -> String:
611 match pl:
612 case Plan.Committed{}:
613 ".ez/lib"
614 case Plan.Cache{}:
615 lib
616 case Plan.Under{dir}:
617 dir ++ "/.ez/lib"
618
619
620def exec(+lib: String, effect: Plan.Effect) -> IO(Unit):
621 match effect:
622 case Plan.Write{at, text}:
623 write(at, text)
624 case Plan.Lay{pl, hash, fs}:
625 +dir = place(lib, pl) ++ "/" ++ hash
626 do IO<Unit>:
627 _rm : String <- R.exec(["rm", "-rf", dir])
628 lay.all(dir, fs)
629 case Plan.Drop{hash}:
630 do IO<Unit>:
631 _rm : String <- R.exec(["rm", "-rf", ".ez/lib/" ++ hash])
632 return Unit{}
633 case Plan.Say{text}:
634 IO.print(text)
635 case Plan.Name{nv, hash}:
636 write(lib ++ "/names/" ++ nv, L.name.text(hash))
637
638
639def exec.all(+lib: String, es: List<&2, Plan.Effect>) -> IO(Unit):
640 match es:
641 case []:
642 IO.pure(Unit, Unit{})
643 case h <> t:
644 do IO<Unit>:
645 exec(lib, h)
646 exec.all(lib, t)
647
648
649
650def end.at(zero: Bool, +code: U32, +why: String) -> IO(Unit):
651 match zero:
652 case True{}:
653 IO.pure(Unit, Unit{})
654 case False{}:
655 IO.die(Unit, code, why)
656
657
658
659def end(+outcome: Plan.Outcome) -> IO(Unit):
660 +code = Plan.status(outcome)
661 end.at(U32.is_eq(code, 0), code, Plan.why(outcome))
662
663
664def exec.plan(+lib: String, pl: Plan.Plan) -> IO(Unit):
665 Plan.Plan{es, outcome} = pl
666 do IO<Unit>:
667 exec.all(lib, es)
668 end(outcome)
669
670
671
672
673
674
675def loop.step(st: Plan.Step, +lib: String, back: WsUp.Back, world: W.World, go: W.World -> IO(Unit)) -> IO(Unit):
676 match st:
677 case Plan.Asking{asks, ups, names}:
678 do IO<Unit>:
679 rs : List<&2, W.Reply> <- answer.all(lib, asks)
680 us : List<&2, Up.Reply> <- answer.queries(lib, ups)
681 ns : List<&2, W.Reply> <- answer.all(lib, names)
682 go(more(world, List.append(&2, W.Reply, rs, ns), us))
683 case Plan.Run{_pl}:
684 exec.plan(lib, WsUp.plan(back, world))
685
686
687
688
689
690
691
692def rounds() -> Nat:
693 U32.to_nat(100000)
694
695
696
697def loop(fuel: Nat, +lib: String, +back: WsUp.Back, +world: W.World) -> IO(Unit):
698 match fuel:
699 case 0n:
700 IO.die(Unit, 1, "ez: the lock kept asking questions")
701 case 1n+f:
702 loop.step(WsUp.step(back, world), lib, back, world, w => loop(f, lib, back, w))
703
704
705
706
707def settle.seen(got: Maybe<&2, String>, ask: W.Ask, answer: W.Answer, +lib: String, +hash: String) -> IO(W.Reply):
708 match got:
709 case None{}:
710 IO.pure(W.Reply, W.Reply{ask, answer})
711 case Some{text}:
712 do IO<W.Reply>:
713 a : W.Answer <- answer.lib(lib ++ "/" ++ hash, text)
714 return W.Reply{ask, a}
715
716
717def settle.one(rp: W.Reply, +lib: String) -> IO(W.Reply):
718 W.Reply{+ask, answer} = rp
719 +hash = W.ask.hash(ask)
720 do IO<W.Reply>:
721 got : Maybe<&2, String> <- F.read(lib ++ "/" ++ hash ++ "/manifest")
722 settle.seen(got, ask, answer, lib, hash)
723
724
725def settle(rs: List<&2, W.Reply>, +lib: String) -> IO(List<&2, W.Reply>):
726 match rs:
727 case []:
728 IO.pure(List<&2, W.Reply>, [])
729 case hd <> tl:
730 do IO<List<&2, W.Reply>>:
731 rp : W.Reply <- settle.one(hd, lib)
732 rest : List<&2, W.Reply> <- settle(tl, lib)
733 return rp <> rest
734
735
736
737def run.planned(pd: WsRun.Planned, +lib: String, up: Bool, only: String, here: String, ls: W.Listing) -> IO(Unit):
738 WsRun.Planned{led, back, seeds} = pd
739 do IO<Unit>:
740 rs : List<&2, W.Reply> <- settle(seeds, lib)
741 loop(rounds(), lib, back, W.World{W.Args{up, only, here}, led, ls, rs, []})
742
743
744
745
746def run(up: Bool, +only: String) -> IO(Unit):
747 do IO<Unit>:
748 IO.print_err(Say.resolving())
749 lib : String <- bend.lib()
750 +pwd : String <- R.exec(["pwd"])
751 own : Maybe<&2, String> <- read.ledger()
752 +ls : W.Listing <- read.listing()
753 pd : WsRun.Planned <- WsRun.ledger(own, J.files(ls))
754 run.planned(pd, lib, up, only, String.trim(R.text(pwd)), ls)