src/ws/root.bend

ezx@1.10.0.0 · 105 lines · 0 definitions

1# ws/root: which workspace a command run in a directory belongs to
2# (EZ-WS-2). ez reads ez.toml from the directory it runs in; in a workspace
3# that directory may be a member, so a command first finds its root: the
4# nearest directory at or above it, up to the top of the git checkout, whose
5# ledger is a workspace root that makes it a member. With none, the
6# directory is a project of its own, as it always was. The design is
7# docs/rfc/ez-workspaces.md.
8#
9# Both halves are pure. `asks` names the directories whose ez.toml the
10# interpreter reads, each with the path from it down to where ez runs, nearest
11# first; `root.go` decides from what was read, in that order. A path is its
12# segments, relative to the top of the checkout, as `git rev-parse
13# --show-prefix` prints it (`here.of`).
14import Base
15import ezx@1.10.0.0/src/ws/workspace.bend as W
16
17# a directory at or above where ez runs: its path from the top of the
18# checkout, and the path from it down to where ez runs
19type Ask is Data:
20  Ask{dir: List<&2, String>, rel: List<&2, String>}
21
22# one such directory, read: its ez.toml's text, or None when it has none
23type Up is Data:
24  Up{dir: List<&2, String>, rel: List<&2, String>, text: Maybe<&2, String>}
25
26# where a command run in a directory stands: in a project of its own, in a
27# workspace at `root` as the member `rel` below it (`[]` for the root itself),
28# or nowhere, because a workspace table above it could not be read
29type Found is Data:
30  Alone{}
31  Within{root: List<&2, String>, rel: List<&2, String>}
32  Broken{why: String}
33
34# where ez runs, from what `git rev-parse --show-prefix` printed: the
35# segments before the `/` it ends with, none at the top. Anything else is no
36# answer.
37def here.of(+prefix: String) -> Maybe<&2, List<&2, String>>:
38  W.cut(W.segs.of(prefix), "")
39
40# every directory asked about from one segment further down, now one segment
41# further from the top
42def asks.under(+hd: String, xs: List<&2, Ask>) -> List<&2, Ask>:
43  match xs:
44    case []:
45      []
46    case Ask{ds, rs} <> tl:
47      Ask{hd <> ds, rs} <> asks.under(hd, tl)
48
49# the directories whose ez.toml is read: where ez runs, then each directory
50# above it in turn, ending at the top of the checkout
51def asks(here: List<&2, String>) -> List<&2, Ask>:
52  match here:
53    case []:
54      [Ask{[], []}]
55    case +hd <> +tl:
56      List.append(&2, Ask, asks.under(hd, asks(tl)), [Ask{[], hd <> tl}])
57
58# whether a workspace root makes the directory this far below it a member:
59# the root itself always, and any other when an entry names it and no
60# exclusion does
61def claims(+spec: W.Spec, rel: List<&2, String>) -> Bool:
62  match rel:
63    case []:
64      True{}
65    case +hd <> +tl:
66      W.chosen(spec, hd <> tl)
67
68# a workspace root claims the directory, so the search ends there, or does
69# not, and goes on above it
70def root.claim(hit: Bool, +dir: List<&2, String>, +rel: List<&2, String>, rest: Found) -> Found:
71  match hit:
72    case True{}:
73      Within{dir, rel}
74    case False{}:
75      rest
76
77# what one directory's ledger says, against what the directories above it
78# decided
79def root.read(rd: W.Read, +dir: List<&2, String>, +rel: List<&2, String>, rest: Found) -> Found:
80  match rd:
81    case W.Lone{}:
82      rest
83    case W.Bad{why}:
84      Broken{why}
85    case W.Root{+spec}:
86      root.claim(claims(spec, rel), dir, rel, rest)
87
88# one directory, with or without a ledger
89def root.at(text: Maybe<&2, String>, +dir: List<&2, String>, +rel: List<&2, String>, rest: Found) -> Found:
90  match text:
91    case None{}:
92      rest
93    case Some{tt}:
94      root.read(W.spec.of(tt), dir, rel, rest)
95
96# the root, from the directories read, nearest first: the first whose ledger
97# is a workspace root that claims where ez runs, a table that could not be
98# read if one comes first, and a project of its own when there is neither
99def root.go(ups: List<&2, Up>) -> Found:
100  match ups:
101    case []:
102      Alone{}
103    case Up{ds, rs, tx} <> tl:
104      rest = root.go(tl)
105      root.at(tx, ds, rs, rest)