src/fetch/world.bend

ezx@1.11.0.0 · 146 lines · 18 definitions

1# fetch/world: what `ez fetch` reads. The directory the project is in
2# (`here`), which a relative path in the lock is anchored to; whether there is
3# an ez.toml, since fetch fills BEND_LIB for a project and nothing else; the
4# lock's text, None when there is no ez.lock.toml; and the answers to the
5# questions the planner (fetch/plan.bend) asked. There is no field for
6# BEND_LIB's path, the environment or the clock, because the planner reads
7# none of them.
8#
9# A question is about one package of the lock, by its hash: the tree already
10# under BEND_LIB (`Laid`), or the package fetched from where the lock says it
11# comes from. A git package is the upgrade's own `Clone` question
12# (lock/up.bend), a shallow checkout at the pinned rev with every file's
13# text; a hub package is the lock's own question (lock/world.bend), the
14# hub's manifest and every file it names. `Laid` and a hub package are
15# answered the way the lock answers its questions, with a manifest and the
16# texts of the files it names, and a clone the way the upgrade answers
17# `Clone`.
18#
19# A name the lock records is asked about too: the file bend reads its hash
20# from, `$BEND_LIB/names/<nv>` (`Named`), as it is, or that there is none.
21#
22# A workspace member's package (EZ-WS-8) is asked about as the committed
23# `.bend` files (`Tree`), answered the way `ez lock` lists them, and the
24# planner walks the member from its entry over them itself.
25import Base
26import ezx@1.11.0.0/src/toml/toml.bend as T
27import ezx@1.11.0.0/src/lock/lock.bend as L
28import ezx@1.11.0.0/src/lock/world.bend as W
29import ezx@1.11.0.0/src/lock/up.bend as Up
30
31# a question about one package of the lock, or one name it records
32type Ask is Data:
33  Laid{hash: String}               # the tree under BEND_LIB/<hash>, as its manifest names it
34  Git{hash: String, ask: Up.Ask}   # a checkout at the pinned rev (`Up.Clone`)
35  Hub{ask: W.Ask}                  # the hub's manifest and every file it names (`W.Pkg`)
36  Named{nv: String}                # the file BEND_LIB/names/<nv>, as it is
37  Tree{hash: String, entry: String}  # the committed files, a member's package walked over them
38
39# the answer to one question: a manifest and the texts of the files it
40# names, what a clone answered, or a names file's text, None when there is
41# none
42type Answer is Data:
43  Read{answer: W.Answer}           # Laid, Hub
44  Said{answer: Up.Answer}          # Git
45  Held{got: Maybe<&2, String>}     # Named
46  Seen{listing: W.Listing}         # Tree
47
48# one question, answered
49type Reply is Data:
50  Reply{ask: Ask, answer: Answer}
51
52# everything `ez fetch` reads
53type World is Data:
54  World{here: String, ledger: Bool, lock: Maybe<&2, String>, replies: List<&2, Reply>}
55
56# the ledger's path, which is fixed
57def toml() -> String:
58  "ez.toml"
59
60# the lock's path, which is fixed
61def lockfile() -> String:
62  "ez.lock.toml"
63
64# the directory the project is in, which every relative path in the lock is
65# read against
66def here(world: World) -> String:
67  World{h, _ledger, _lock, _replies} = world
68  h
69
70# whether there is an ez.toml
71def ledger(world: World) -> Bool:
72  World{_here, l, _lock, _replies} = world
73  l
74
75# the lock's text, None when there is no ez.lock.toml
76def lock(world: World) -> Maybe<&2, String>:
77  World{_here, _ledger, l, _replies} = world
78  l
79
80# every question answered so far
81def replies(world: World) -> List<&2, Reply>:
82  World{_here, _ledger, _lock, rs} = world
83  rs
84
85# a lock's text, as tables; none when there is no lock
86def sects.of(got: Maybe<&2, String>) -> List<&2, T.Sect>:
87  match got:
88    case None{}:
89      []
90    case Some{text}:
91      T.sects(T.parse(text))
92
93# the lock, as tables
94def sects(world: World) -> List<&2, T.Sect>:
95  sects.of(lock(world))
96
97# a hub question as a key
98def hub.key(ask: W.Ask) -> String:
99  match ask:
100    case W.Pkg{hash, _src, hub}:
101      "hub\n" ++ hash ++ "\n" ++ hub
102    case W.Name{nv, hub}:
103      "hubname\n" ++ nv ++ "\n" ++ hub
104    case W.Lock{}:
105      "hublock"
106
107# a question as a key. A lock's value never holds a newline, so the fields
108# are kept apart by one.
109def key(ask: Ask) -> String:
110  match ask:
111    case Laid{hash}:
112      "laid\n" ++ hash
113    case Git{hash, q}:
114      "git\n" ++ hash ++ "\n" ++ Up.key(q)
115    case Hub{q}:
116      hub.key(q)
117    case Named{nv}:
118      "named\n" ++ nv
119    case Tree{hash, _entry}:
120      "tree\n" ++ hash
121
122# an answer looked up, or none yet
123type Heard is Data:
124  Heard{answer: Answer}
125  Open{}
126
127# this answer when it is the question's, otherwise the rest's. The rest
128# arrives as a thunk, so the first hit ends the scan.
129def look.step(hit: Bool, answer: Answer, rest: Unit -> Heard) -> Heard:
130  match hit:
131    case True{}:
132      Heard{answer}
133    case False{}:
134      rest(Unit{})
135
136# the first answer given to a question
137def look(rs: List<&2, Reply>, +want: String) -> Heard:
138  match rs:
139    case []:
140      Open{}
141    case Reply{ask, answer} <> t:
142      look.step(String.eq(key(ask), want), answer, _u => look(t, want))
143
144# the answer to a question, or none yet
145def heard(rs: List<&2, Reply>, ask: Ask) -> Heard:
146  look(rs, key(ask))