1 # add/world: what `ez add` reads. The arguments it was given; the directory 2 # it runs in (`here`), which every git question anchors a relative path to; 3 # HOME, which a `~/` target is expanded against; the ledger's text, None when 4 # there is no ez.toml; `.gitignore`'s text, "" when there is none; and the 5 # answers to the questions the planner (add/plan.bend) asked. The questions 6 # and answers are the upgrade's (lock/up.bend), which the lock's interpreter 7 # answers: `Tags`, `Head` and `Refs` for what a ref names, and `Clone` for the 8 # checkout at the rev, with its NAR hash and every file's text. A hub 9 # package added by its `@` (add/hub.bend) asks the lock's own 10 # questions instead (lock/world.bend), which the lock's interpreter answers 11 # too: `Name` for the hash the hub says the name names, and `Pkg` for the 12 # package's manifest and files, served by the hub. Those answers are `hubs`. 13 # 14 # Everything here is a projection: what a World says, read one way. The 15 # planner is written over these, so each law about the plan is a law about 16 # them. 17 import Base 18 import ../lock/up.bend as Up 19 import ../lock/world.bend as W 20 import ../ledger/manifest.bend as M 21 import ../pkg/path.bend as P 22 import ../ez/target.bend as Tgt 23 import ../ez/named.bend as Named 24 25 # what `ez add [ref] [entry.bend] [--rename NAME]` was given, each 26 # "" when it was not 27 type Args is Data: 28 Args{target: String, ref: String, entry: String, rename: String} 29 30 # everything `ez add` reads 31 type World is Data: 32 World{args: Args, here: String, home: String, ledger: Maybe<&2, String>, 33 ignore: String, replies: List<&2, Up.Reply>, hubs: List<&2, W.Reply>} 34 35 # the ledger's path, which is fixed 36 def toml() -> String: 37 "ez.toml" 38 39 # the ignore file's path, which is fixed 40 def ignore.file() -> String: 41 ".gitignore" 42 43 # the arguments 44 def args(world: World) -> Args: 45 World{a, _here, _home, _ledger, _ignore, _replies, _hubs} = world 46 a 47 48 def args.target(given: Args) -> String: 49 Args{t, _ref, _entry, _rename} = given 50 t 51 52 def args.ref(given: Args) -> String: 53 Args{_t, r, _entry, _rename} = given 54 r 55 56 def args.entry(given: Args) -> String: 57 Args{_t, _ref, e, _rename} = given 58 e 59 60 def args.rename(given: Args) -> String: 61 Args{_t, _ref, _entry, r} = given 62 r 63 64 # the target asked for 65 def target(world: World) -> String: 66 args.target(args(world)) 67 68 # the ref asked for, "" for none 69 def ref(world: World) -> String: 70 args.ref(args(world)) 71 72 def entry.arg(world: World) -> String: 73 args.entry(args(world)) 74 75 # the `--rename` asked for, "" for none 76 def rename(world: World) -> String: 77 args.rename(args(world)) 78 79 # the directory ez runs in, which is the project root 80 def here(world: World) -> String: 81 World{_args, h, _home, _ledger, _ignore, _replies, _hubs} = world 82 h 83 84 # HOME, which a `~/` target is expanded against 85 def home(world: World) -> String: 86 World{_args, _here, h, _ledger, _ignore, _replies, _hubs} = world 87 h 88 89 # the ledger's text, None when there is no ez.toml 90 def ledger(world: World) -> Maybe<&2, String>: 91 World{_args, _here, _home, l, _ignore, _replies, _hubs} = world 92 l 93 94 # `.gitignore`'s text, "" when there is none 95 def ignore(world: World) -> String: 96 World{_args, _here, _home, _ledger, i, _replies, _hubs} = world 97 i 98 99 # every question answered so far 100 def replies(world: World) -> List<&2, Up.Reply>: 101 World{_args, _here, _home, _ledger, _ignore, rs, _hubs} = world 102 rs 103 104 # every hub question answered so far 105 def hubs(world: World) -> List<&2, W.Reply>: 106 World{_args, _here, _home, _ledger, _ignore, _replies, hs} = world 107 hs 108 109 # the ledger as read: what the file says, or, with no file, a read that did 110 # not parse and says why. A missing ledger is not an empty one to write back. 111 def read.of(got: Maybe<&2, String>) -> M.Read: 112 match got: 113 case None{}: 114 M.Bad{"no " ++ toml() ++ " here; ez add works on a project's " ++ 115 "ledger, and ez init makes one"} 116 case Some{text}: 117 M.parse(text) 118 119 # the ledger, read 120 def read(world: World) -> M.Read: 121 read.of(ledger(world)) 122 123 # the model of a ledger that read; an empty one otherwise, which nothing 124 # writes, since a ledger that did not read is refused 125 def manifest.of(led: M.Read) -> M.Manifest: 126 match led: 127 case M.Bad{_why}: 128 M.Manifest{"", "", "", "", "", "", [], []} 129 case M.Good{m}: 130 m 131 132 # the dependencies of a ledger that was read 133 def deps.of(manifest: M.Manifest) -> List<&2, M.Dep>: 134 M.Manifest{_n, _e, _b, _h, _pa, _pv, ds, _ts} = manifest 135 ds 136 137 # the ledger's dependencies, none when it did not read 138 def deps(world: World) -> List<&2, M.Dep>: 139 deps.of(manifest.of(read(world))) 140 141 # where the target names 142 def place(world: World) -> Tgt.Place: 143 Tgt.classify(target(world)) 144 145 # the source a place records: a URL as it is, a path as it was given with a 146 # leading `~/` expanded against HOME, and nothing for a target that names 147 # nothing 148 def url.of(place: Tgt.Place, +home: String) -> String: 149 match place: 150 case Tgt.Bad{_why}: 151 "" 152 case Tgt.Local{path}: 153 Tgt.expand(home, path) 154 case Tgt.Remote{url, _slug}: 155 url 156 157 # the source the ledger records (EZ-LED-8) 158 def url(+world: World) -> String: 159 url.of(place(world), home(world)) 160 161 # the source as git is to read it: a relative path taken from where ez runs, 162 # which is the project root. Every question the planner asks carries this. 163 def from(+world: World) -> String: 164 P.anchor(here(world), url(world)) 165 166 # the name the source gives itself: a repository's, or the directory's the 167 # path resolves to 168 def fresh(+world: World) -> String: 169 Named.called(place(world), from(world)) 170 171 # what the target names: a hub package by its `@`, or a place 172 def aim(world: World) -> Tgt.Aim: 173 Tgt.aim(target(world)) 174 175 # whether the target is a hub package's `@` 176 def named(world: World) -> Bool: 177 Tgt.aim.named(aim(world)) 178 179 # the hub the ledger names, which a name and its package are asked of 180 def hub(world: World) -> String: 181 M.hub_of(read(world))