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/` (`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. 25 import Base 26 import ../toml/toml.bend as T 27 import ../lock/lock.bend as L 28 import ../lock/world.bend as W 29 import ../lock/up.bend as Up 30 31 # a question about one package of the lock, or one name it records 32 type Ask is Data: 33 Laid{hash: String} # the tree under BEND_LIB/, 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/, 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 42 type 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 49 type Reply is Data: 50 Reply{ask: Ask, answer: Answer} 51 52 # everything `ez fetch` reads 53 type 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 57 def toml() -> String: 58 "ez.toml" 59 60 # the lock's path, which is fixed 61 def 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 66 def here(world: World) -> String: 67 World{h, _ledger, _lock, _replies} = world 68 h 69 70 # whether there is an ez.toml 71 def 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 76 def lock(world: World) -> Maybe<&2, String>: 77 World{_here, _ledger, l, _replies} = world 78 l 79 80 # every question answered so far 81 def 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 86 def 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 94 def sects(world: World) -> List<&2, T.Sect>: 95 sects.of(lock(world)) 96 97 # a hub question as a key 98 def 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. 109 def 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 123 type 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. 129 def 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 137 def 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 145 def heard(rs: List<&2, Reply>, ask: Ask) -> Heard: 146 look(rs, key(ask))