src/ledger/manifest.bend
ezx@1.11.0.0 · 502 lines · 67 definitions
1
2
3
4
5
6import Base
7import ezx@1.11.0.0/src/toml/toml.bend as T
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28type Source is Data:
29 Hub{named: String}
30 Git{url: String, rev: String, tag: String, root: String, nar: String, vend: Bool}
31 Member{path: String, named: String}
32
33
34type Dep is Data:
35 Dep{name: String, hash: String, entry: String, source: Source}
36
37
38
39
40
41type Tool is Data:
42 Tool{name: String, entry: String, bin: String, source: Source}
43
44
45
46
47
48
49type Manifest is Data:
50 Manifest{name: String, entry: String, bin: String, hub: String,
51 publish: String, version: String,
52 deps: List<&2, Dep>, tools: List<&2, Tool>}
53
54
55type Read is Data:
56 Bad{why: String}
57 Good{m: Manifest}
58
59
60def value(pairs: List<&2, T.Kv>, key: String) -> String:
61 T.value(pairs, key)
62
63
64def pairs.at(sect: T.Sect, rest: List<&2, T.Kv>, name: String) -> List<&2, T.Kv>:
65 T.Sect{n, ps} = sect
66 Bool.pick(List<&2, T.Kv>, String.eq(n, name), ps, rest)
67
68
69def pairs_of(ss: List<&2, T.Sect>, +name: String) -> List<&2, T.Kv>:
70 match ss:
71 case []:
72 []
73 case h <> t:
74 pairs.at(h, pairs_of(t, name), name)
75
76
77def flag(+word: String) -> Bool:
78 String.eq(word, "true")
79
80
81def source.go(hub: Bool, url: String, rev: String, tag: String, root: String, nar: String, vend: Bool) -> Source:
82 match hub:
83 case True{}:
84 Hub{""}
85 case False{}:
86 Git{url, rev, tag, root, nar, vend}
87
88
89
90
91def source.named(named: String, src: Source) -> Source:
92 match src:
93 case Hub{_old}:
94 Hub{named}
95 case Git{url, rev, tag, root, nar, vend}:
96 Git{url, rev, tag, root, nar, vend}
97 case Member{path, nv}:
98 Member{path, nv}
99
100
101
102
103def root.or(+dir: String) -> String:
104 Bool.pick(String, String.is_empty(dir), ".", dir)
105
106
107
108def source.ws(member: Bool, path: String, named: String, other: Source) -> Source:
109 match member:
110 case True{}:
111 Member{path, named}
112 case False{}:
113 other
114
115
116def source_of(+pairs: List<&2, T.Kv>) -> Source:
117 source.go(String.is_empty(value(pairs, "git")), value(pairs, "git"),
118 value(pairs, "rev"), value(pairs, "tag"), root.or(value(pairs, "root")),
119 value(pairs, "narHash"), flag(value(pairs, "vendor")))
120
121
122
123
124def dep_of(+full: String, +pairs: List<&2, T.Kv>) -> Dep:
125 Dep{String.drop(full, 5n), value(pairs, "hash"), value(pairs, "entry"),
126 source.ws(flag(value(pairs, "workspace")), value(pairs, "path"), value(pairs, "hub"),
127 source.named(value(pairs, "hub"), source_of(pairs)))}
128
129
130def dep.name(dependency: Dep) -> String:
131 Dep{n, _h, _e, _s} = dependency
132 n
133
134
135def dep.hash(dependency: Dep) -> String:
136 Dep{_n, h, _e, _s} = dependency
137 h
138
139
140
141def dep.entry(dependency: Dep) -> String:
142 Dep{_n, _h, e, _s} = dependency
143 e
144
145
146def deps.put(is_dep: Bool, sect: T.Sect, rest: List<&2, Dep>) -> List<&2, Dep>:
147 match is_dep:
148 case True{}:
149 T.Sect{n, ps} = sect
150 dep_of(n, ps) <> rest
151 case False{}:
152 rest
153
154
155def name_of(sect: T.Sect) -> String:
156 T.Sect{n, _ps} = sect
157 n
158
159
160def deps(ss: List<&2, T.Sect>) -> List<&2, Dep>:
161 match ss:
162 case +h <> t:
163 deps.put(String.starts_with(name_of(h), "deps."), h, deps(t))
164 case []:
165 []
166
167
168def tool_of(+full: String, +pairs: List<&2, T.Kv>) -> Tool:
169 Tool{String.drop(full, 6n), value(pairs, "entry"), value(pairs, "bin"),
170 source_of(pairs)}
171
172
173def tool.name(pin: Tool) -> String:
174 Tool{n, _e, _b, _s} = pin
175 n
176
177
178def tool.entry(pin: Tool) -> String:
179 Tool{_n, e, _b, _s} = pin
180 e
181
182
183def tool.bin(pin: Tool) -> String:
184 Tool{_n, _e, b, _s} = pin
185 b
186
187
188def tool.source(pin: Tool) -> Source:
189 Tool{_n, _e, _b, s} = pin
190 s
191
192
193def tools.put(is_tool: Bool, sect: T.Sect, rest: List<&2, Tool>) -> List<&2, Tool>:
194 match is_tool:
195 case True{}:
196 T.Sect{n, ps} = sect
197 tool_of(n, ps) <> rest
198 case False{}:
199 rest
200
201
202def tools(ss: List<&2, T.Sect>) -> List<&2, Tool>:
203 match ss:
204 case +h <> t:
205 tools.put(String.starts_with(name_of(h), "tools."), h, tools(t))
206 case []:
207 []
208
209
210def missing.at(bad: Bool, name: String, rest: String) -> String:
211 match bad:
212 case True{}:
213 name
214 case False{}:
215 rest
216
217
218def missing.is(sect: T.Sect) -> Bool:
219 T.Sect{n, +ps} = sect
220 Bool.and(String.starts_with(n, "deps."),
221 Bool.and(Bool.not(flag(value(ps, "workspace"))), String.is_empty(value(ps, "hash"))))
222
223
224def missing(ss: List<&2, T.Sect>) -> String:
225 match ss:
226 case +h <> t:
227 missing.at(missing.is(h), name_of(h), missing(t))
228 case []:
229 ""
230
231
232
233def problem(+bad: String, +miss: String) -> String:
234 Bool.pick(String, String.is_empty(bad),
235 Bool.pick(String, String.is_empty(miss), "", miss ++ " has no hash"),
236 bad)
237
238
239def build(+ss: List<&2, T.Sect>) -> Read:
240 Good{Manifest{value(pairs_of(ss, "package"), "name"),
241 value(pairs_of(ss, "package"), "entry"),
242 value(pairs_of(ss, "package"), "bin"),
243 value(pairs_of(ss, "package"), "hub"),
244 value(pairs_of(ss, "package"), "publish-as"),
245 value(pairs_of(ss, "package"), "version"), deps(ss), tools(ss)}}
246
247
248def read.go(ok: Bool, why: String, ss: List<&2, T.Sect>) -> Read:
249 match ok:
250 case True{}:
251 build(ss)
252 case False{}:
253 Bad{why}
254
255
256def read(+bad: String, +ss: List<&2, T.Sect>) -> Read:
257 read.go(String.is_empty(problem(bad, missing(ss))),
258 problem(bad, missing(ss)), ss)
259
260
261def parse.go(toml: T.Toml) -> Read:
262 T.Toml{bad, _name, _pairs, sects} = toml
263 read(bad, sects)
264
265
266def parse(text: String) -> Read:
267 parse.go(T.parse(text))
268
269
270def show.source(src: Source) -> String:
271 match src:
272 case Hub{_named}:
273 "hub"
274 case Git{_url, _rev, _tag, _root, _nar, _vend}:
275 "git"
276 case Member{_path, _named}:
277 "member"
278
279
280def show.dep(dependency: Dep) -> String:
281 Dep{n, h, _entry, src} = dependency
282 n ++ "=" ++ h ++ "@" ++ show.source(src)
283
284
285
286def show.deps(ds: List<&2, Dep>) -> List<&2, String>:
287 match ds:
288 case []:
289 []
290 case h <> t:
291 show.dep(h) <> show.deps(t)
292
293
294def show.m(manifest: Manifest) -> String:
295 Manifest{n, e, _b, _h, _pa, _pv, ds, _ts} = manifest
296 n ++ " " ++ e ++ " " ++ String.join(show.deps(ds), " ")
297
298
299def show(ledger: Read) -> String:
300 match ledger:
301 case Bad{why}:
302 "error: " ++ why
303 case Good{m}:
304 show.m(m)
305
306
307def find.pick(hit: Bool, dependency: Dep, rest: Dep) -> Dep:
308 match hit:
309 case True{}:
310 dependency
311 case False{}:
312 rest
313
314
315def find.at(+dependency: Dep, rest: Dep, name: String) -> Dep:
316 Dep{n, _h, _e, _s} = dependency
317 find.pick(String.eq(n, name), dependency, rest)
318
319
320def find(ds: List<&2, Dep>, +name: String) -> Dep:
321 match ds:
322 case []:
323 Dep{"", "", "", Hub{""}}
324 case h <> t:
325 find.at(h, find(t, name), name)
326
327
328def dep(ledger: Read, name: String) -> Dep:
329 match ledger:
330 case Bad{_why}:
331 Dep{"", "", "", Hub{""}}
332 case Good{m}:
333 Manifest{_n, _e, _b, _h, _pa, _pv, ds, _ts} = m
334 find(ds, name)
335
336
337def source.rev(src: Source) -> String:
338 match src:
339 case Hub{_named}:
340 ""
341 case Git{_url, rev, _tag, _root, _nar, _vend}:
342 rev
343 case Member{_path, _named}:
344 ""
345
346
347def source.tag(src: Source) -> String:
348 match src:
349 case Hub{_named}:
350 ""
351 case Git{_url, _rev, tag, _root, _nar, _vend}:
352 tag
353 case Member{_path, _named}:
354 ""
355
356
357def source.url(src: Source) -> String:
358 match src:
359 case Hub{_named}:
360 ""
361 case Git{url, _rev, _tag, _root, _nar, _vend}:
362 url
363 case Member{_path, _named}:
364 ""
365
366
367def source.root(src: Source) -> String:
368 match src:
369 case Hub{_named}:
370 ""
371 case Git{_url, _rev, _tag, root, _nar, _vend}:
372 root
373 case Member{_path, _named}:
374 ""
375
376
377def source.nar(src: Source) -> String:
378 match src:
379 case Hub{_named}:
380 ""
381 case Git{_url, _rev, _tag, _root, nar, _vend}:
382 nar
383 case Member{_path, _named}:
384 ""
385
386
387
388def source.nv(src: Source) -> String:
389 match src:
390 case Hub{named}:
391 named
392 case Git{_url, _rev, _tag, _root, _nar, _vend}:
393 ""
394 case Member{_path, named}:
395 named
396
397
398def source.is_git(src: Source) -> Bool:
399 match src:
400 case Hub{_named}:
401 False{}
402 case Git{_url, _rev, _tag, _root, _nar, _vend}:
403 True{}
404 case Member{_path, _named}:
405 False{}
406
407
408def source.vendor(src: Source) -> Bool:
409 match src:
410 case Hub{_named}:
411 False{}
412 case Git{_url, _rev, _tag, _root, _nar, vend}:
413 vend
414 case Member{_path, _named}:
415 False{}
416
417
418def source.dep(dependency: Dep) -> Source:
419 Dep{_n, _h, _e, s} = dependency
420 s
421
422
423def rev_of(ledger: Read, name: String) -> String:
424 source.rev(source.dep(dep(ledger, name)))
425
426
427def tag_of(ledger: Read, name: String) -> String:
428 source.tag(source.dep(dep(ledger, name)))
429
430
431
432
433
434def hub.or(+url: String) -> String:
435 Bool.pick(String, String.is_empty(url), "https://hub.bend-lang.com", url)
436
437
438
439def hub_of(ledger: Read) -> String:
440 match ledger:
441 case Bad{_why}:
442 hub.or("")
443 case Good{m}:
444 Manifest{_n, _e, _b, h, pa, pv, _ds, _ts} = m
445 hub.or(h)
446
447
448def tools_of(ledger: Read) -> List<&2, Tool>:
449 match ledger:
450 case Bad{_why}:
451 []
452 case Good{m}:
453 Manifest{_n, _e, _b, _h, _pa, _pv, _ds, ts} = m
454 ts
455
456
457def tool.find.pick(hit: Bool, pin: Tool, rest: Tool) -> Tool:
458 match hit:
459 case True{}:
460 pin
461 case False{}:
462 rest
463
464
465def tool.find.at(+pin: Tool, rest: Tool, +name: String) -> Tool:
466 tool.find.pick(String.eq(tool.name(pin), name), pin, rest)
467
468
469def tool.find(ts: List<&2, Tool>, +name: String) -> Tool:
470 match ts:
471 case []:
472 Tool{"", "", "", Hub{""}}
473 case h <> t:
474 tool.find.at(h, tool.find(t, name), name)
475
476
477def tool.step(hit: Bool, rest: Unit -> Bool) -> Bool:
478 match hit:
479 case True{}:
480 True{}
481 case False{}:
482 rest(Unit{})
483
484
485def tool.has(ts: List<&2, Tool>, +name: String) -> Bool:
486 match ts:
487 case []:
488 False{}
489 case +h <> t:
490 tool.step(String.eq(tool.name(h), name), _u => tool.has(t, name))
491
492
493def tool.known.go(all: Bool, ts: List<&2, Tool>, +name: String) -> Bool:
494 match all:
495 case True{}:
496 True{}
497 case False{}:
498 tool.has(ts, name)
499
500
501def tool.known(+only: String, ts: List<&2, Tool>) -> Bool:
502 tool.known.go(String.is_empty(only), ts, only)