src/fetch/world.bend

ezx@1.9.0.0 · 138 lines · 0 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.
21import Base
22import ezx@1.9.0.0/src/toml/toml.bend as T
23import ezx@1.9.0.0/src/lock/lock.bend as L
24import ezx@1.9.0.0/src/lock/world.bend as W
25import ezx@1.9.0.0/src/lock/up.bend as Up
26
27# a question about one package of the lock, or one name it records
28type Ask is Data:
29  Laid{hash: String}               # the tree under BEND_LIB/<hash>, as its manifest names it
30  Git{hash: String, ask: Up.Ask}   # a checkout at the pinned rev (`Up.Clone`)
31  Hub{ask: W.Ask}                  # the hub's manifest and every file it names (`W.Pkg`)
32  Named{nv: String}                # the file BEND_LIB/names/<nv>, as it is
33
34# the answer to one question: a manifest and the texts of the files it
35# names, what a clone answered, or a names file's text, None when there is
36# none
37type Answer is Data:
38  Read{answer: W.Answer}           # Laid, Hub
39  Said{answer: Up.Answer}          # Git
40  Held{got: Maybe<&2, String>}     # Named
41
42# one question, answered
43type Reply is Data:
44  Reply{ask: Ask, answer: Answer}
45
46# everything `ez fetch` reads
47type World is Data:
48  World{here: String, ledger: Bool, lock: Maybe<&2, String>, replies: List<&2, Reply>}
49
50# the ledger's path, which is fixed
51def toml() -> String:
52  "ez.toml"
53
54# the lock's path, which is fixed
55def lockfile() -> String:
56  "ez.lock.toml"
57
58# the directory the project is in, which every relative path in the lock is
59# read against
60def here(world: World) -> String:
61  World{h, _ledger, _lock, _replies} = world
62  h
63
64# whether there is an ez.toml
65def ledger(world: World) -> Bool:
66  World{_here, l, _lock, _replies} = world
67  l
68
69# the lock's text, None when there is no ez.lock.toml
70def lock(world: World) -> Maybe<&2, String>:
71  World{_here, _ledger, l, _replies} = world
72  l
73
74# every question answered so far
75def replies(world: World) -> List<&2, Reply>:
76  World{_here, _ledger, _lock, rs} = world
77  rs
78
79# a lock's text, as tables; none when there is no lock
80def sects.of(got: Maybe<&2, String>) -> List<&2, T.Sect>:
81  match got:
82    case None{}:
83      []
84    case Some{text}:
85      T.sects(T.parse(text))
86
87# the lock, as tables
88def sects(world: World) -> List<&2, T.Sect>:
89  sects.of(lock(world))
90
91# a hub question as a key
92def hub.key(ask: W.Ask) -> String:
93  match ask:
94    case W.Pkg{hash, _src, hub}:
95      "hub\n" ++ hash ++ "\n" ++ hub
96    case W.Name{nv, hub}:
97      "hubname\n" ++ nv ++ "\n" ++ hub
98    case W.Lock{}:
99      "hublock"
100
101# a question as a key. A lock's value never holds a newline, so the fields
102# are kept apart by one.
103def key(ask: Ask) -> String:
104  match ask:
105    case Laid{hash}:
106      "laid\n" ++ hash
107    case Git{hash, q}:
108      "git\n" ++ hash ++ "\n" ++ Up.key(q)
109    case Hub{q}:
110      hub.key(q)
111    case Named{nv}:
112      "named\n" ++ nv
113
114# an answer looked up, or none yet
115type Heard is Data:
116  Heard{answer: Answer}
117  Open{}
118
119# this answer when it is the question's, otherwise the rest's. The rest
120# arrives as a thunk, so the first hit ends the scan.
121def look.step(hit: Bool, answer: Answer, rest: Unit -> Heard) -> Heard:
122  match hit:
123    case True{}:
124      Heard{answer}
125    case False{}:
126      rest(Unit{})
127
128# the first answer given to a question
129def look(rs: List<&2, Reply>, +want: String) -> Heard:
130  match rs:
131    case []:
132      Open{}
133    case Reply{ask, answer} <> t:
134      look.step(String.eq(key(ask), want), answer, _u => look(t, want))
135
136# the answer to a question, or none yet
137def heard(rs: List<&2, Reply>, ask: Ask) -> Heard:
138  look(rs, key(ask))