1 import Base 2 import ../lib/effs/io.bend as X 3 import ../lib/vstr.bend as S 4 5 # The project `v` runs in: the current folder. Its own Bend programs (facts, plan, flake, laws...) 6 # are compiled and run from there. 7 8 # Compile one of the project's Bend programs to a native binary, then run it; answers its output. 9 def run_bend(+file: String) -> IO(String): 10 X.sh("$HOME/.bend/bin/bend " ++ file ++ " -o /tmp/v-" ++ file ++ ".bin >/dev/null 2>&1 && /tmp/v-" ++ file ++ ".bin") 11 12 # Type-check a Bend file (the laws); nothing is run. 13 def check_bend(file: String) -> IO(String): 14 X.sh("$HOME/.bend/bin/bend " ++ file ++ " 2>&1 || true") 15 16 # ---- the project's facts (facts.bend): where V's library is, its targets, services and packages ---- 17 type Facts is Data: 18 Facts{vlib: String, targets: List<&2, String>, labels: List<&2, String>, ports: List<&2, String>, packages: List<&2, String>, sources: List<&2, String>} 19 20 def fact_values(+k: String, ls: List<&2, String>) -> List<&2, String>: 21 match ls: 22 case []: 23 [] 24 case +l <> rest: 25 S.append(S.line_if(String.starts_with(l, k ++ " "), String.drop(l, Nat.add(String.length(k), 1n))), fact_values(k, rest)) 26 27 def read_facts() -> IO(Facts): 28 do IO: 29 out : String <- run_bend("facts.bend") 30 +ls : List<&2, String> = String.lines(out) 31 return Facts{S.nth(fact_values("vlib", ls), 0n), fact_values("target", ls), fact_values("label", ls), fact_values("port", ls), fact_values("package", ls), fact_values("source", ls)} 32 33 def vlib_of(f: Facts) -> String: 34 match f: 35 case Facts{v, _, _, _, _, _}: 36 v 37 38 def targets_of(f: Facts) -> List<&2, String>: 39 match f: 40 case Facts{_, t, _, _, _, _}: 41 t 42 43 def labels_of(f: Facts) -> List<&2, String>: 44 match f: 45 case Facts{_, _, l, _, _, _}: 46 l 47 48 def ports_of(f: Facts) -> List<&2, String>: 49 match f: 50 case Facts{_, _, _, p, _, _}: 51 p 52 53 def packages_of(f: Facts) -> List<&2, String>: 54 match f: 55 case Facts{_, _, _, _, p, _}: 56 p 57 58 def sources_of(f: Facts) -> List<&2, String>: 59 match f: 60 case Facts{_, _, _, _, _, sources}: 61 sources 62 63 def target_hit(+name: String, ts: List<&2, String>) -> Bool: 64 match ts: 65 case []: 66 False{} 67 case +t <> rest: 68 String.eq(S.nth(S.words(t), 0n), name) || target_hit(name, rest) 69 70 # Whether the project declares a target ("nix-darwin", "tailscale-serve", "bend-hub"). 71 def has_target(+f: Facts, +name: String) -> Bool: 72 target_hit(name, targets_of(f)) 73 74 def host_in(+name: String, ts: List<&2, String>) -> String: 75 match ts: 76 case []: 77 "" 78 case +t <> rest: 79 S.pick(String.eq(S.nth(S.words(t), 0n), name), S.nth(S.words(t), 1n), host_in(name, rest)) 80 81 # The nix-darwin flake host ("" without a nix-darwin target). 82 def flake_host(+f: Facts) -> String: 83 host_in("nix-darwin", targets_of(f)) 84 85 def github_repo(+f: Facts) -> String: 86 host_in("github", targets_of(f)) 87 88 def target_arg(+name: String, +index: Nat, ts: List<&2, String>) -> String: 89 match ts: 90 case []: 91 "" 92 case +t <> rest: 93 S.pick(String.eq(S.nth(S.words(t), 0n), name), S.nth(S.words(t), index), target_arg(name, index, rest)) 94 95 def github_visibility(+f: Facts) -> String: 96 target_arg("github", 2n, targets_of(f)) 97 98 def azure_organization(+f: Facts) -> String: 99 target_arg("azure", 1n, targets_of(f)) 100 101 def azure_project(+f: Facts) -> String: 102 target_arg("azure", 2n, targets_of(f)) 103 104 def when(c: Bool, act: IO(Unit)) -> IO(Unit): 105 match c: 106 case True{}: 107 act 108 case False{}: 109 IO.pure(Unit, Unit{})