domain/architecture/system/deployment/nix_darwin/brew/ops.bend
near-v-framework@0.5.1.0 · 188 lines · 0 definitions
1import Base
2import near-v-framework@0.5.1.0/domain/architecture/system/deployment/nix_darwin/brew/type.bend as Br
3
4# ---- string literals: Nix (mac.nix), Ruby (the Brewfile) and Bend (an imported settings file) ----
5# One character of a double-quoted literal: backslash, quote and the language's `x` (Nix's $, Ruby's #)
6# are escaped, and so is a newline.
7def quote_char(+c: Char, +x: Char) -> String:
8 Bool.pick(String, Char.is_eq(c, '\\') || Char.is_eq(c, '"') || Char.is_eq(c, x), "\\" ++ String.from_list([c]),
9 Bool.pick(String, Char.is_eq(c, '\n'), "\\n", String.from_list([c])))
10
11def quote_chars(cs: List<&2, Char>, +x: Char) -> String:
12 match cs:
13 case []:
14 ""
15 case c <> rest:
16 quote_char(c, x) ++ quote_chars(rest, x)
17
18# A string's characters as a Nix string literal writes them, without the quotes.
19def nix_esc(s: String) -> String:
20 quote_chars(String.to_list(s), '$')
21
22def nix_str(s: String) -> String:
23 "\"" ++ nix_esc(s) ++ "\""
24
25def ruby_str(s: String) -> String:
26 "\"" ++ quote_chars(String.to_list(s), '#') ++ "\""
27
28def bend_str(s: String) -> String:
29 "\"" ++ quote_chars(String.to_list(s), '"') ++ "\""
30
31# ---- the Homebrew section of mac.nix: one list per kind; entries nix-darwin has no option for are raw
32# Brewfile lines ----
33def tap_nix(b: Br.Brew) -> String:
34 match b:
35 case Br.Tap{n}:
36 " " ++ nix_str(n)
37 case _:
38 ""
39
40def formula_nix(b: Br.Brew) -> String:
41 match b:
42 case Br.Formula{n}:
43 " " ++ nix_str(n)
44 case _:
45 ""
46
47def cask_nix(b: Br.Brew) -> String:
48 match b:
49 case Br.Cask{n}:
50 " " ++ nix_str(n)
51 case _:
52 ""
53
54def other_nix(b: Br.Brew) -> String:
55 match b:
56 case Br.Other{l}:
57 " " ++ l ++ "\n"
58 case _:
59 ""
60
61def mas_name(b: Br.Brew) -> List<&2, String>:
62 match b:
63 case Br.Mas{n, _}:
64 [n]
65 case _:
66 []
67
68def mas_names(xs: List<&2, Br.Brew>) -> List<&2, String>:
69 match xs:
70 case []:
71 []
72 case b <> rest:
73 List.append(&2, String, mas_name(b), mas_names(rest))
74
75def occurrences(xs: List<&2, String>, +x: String) -> Nat:
76 match xs:
77 case []:
78 0n
79 case y <> rest:
80 Nat.add(Bool.pick(Nat, String.eq(x, y), 1n, 0n), occurrences(rest, x))
81
82# masApps is an attribute set: an app name the list repeats gets its id added.
83def mas_nix(b: Br.Brew, +names: List<&2, String>) -> String:
84 match b:
85 case Br.Mas{+n, +id}:
86 " " ++ nix_str(Bool.pick(String, Nat.is_gt(occurrences(names, n), 1n), n ++ " (" ++ id ++ ")", n)) ++ " = " ++ id ++ ";"
87 case _:
88 ""
89
90# Part 0 taps, 1 masApps, 2 brews, 3 casks, 4 extraConfig.
91def all_nix(xs: List<&2, Br.Brew>, +part: Nat, +names: List<&2, String>) -> String:
92 match xs:
93 case []:
94 ""
95 case +b <> rest:
96 Bool.pick(String, Nat.is_eq(part, 0n), tap_nix(b),
97 Bool.pick(String, Nat.is_eq(part, 1n), mas_nix(b, names),
98 Bool.pick(String, Nat.is_eq(part, 2n), formula_nix(b),
99 Bool.pick(String, Nat.is_eq(part, 3n), cask_nix(b), other_nix(b))))) ++ all_nix(rest, part, names)
100
101def nix_section(+xs: List<&2, Br.Brew>) -> String:
102 " homebrew.taps = [" ++ all_nix(xs, 0n, []) ++ " ];\n" ++
103 " homebrew.masApps = {" ++ all_nix(xs, 1n, mas_names(xs)) ++ " };\n" ++
104 " homebrew.brews = [" ++ all_nix(xs, 2n, []) ++ " ];\n" ++
105 " homebrew.casks = [" ++ all_nix(xs, 3n, []) ++ " ];\n" ++
106 " homebrew.extraConfig = ''\n" ++ all_nix(xs, 4n, []) ++ " '';\n"
107
108# ---- the Brewfile, for brew bundle ----
109def brewfile_line(b: Br.Brew) -> String:
110 match b:
111 case Br.Tap{n}:
112 "tap " ++ ruby_str(n)
113 case Br.Formula{n}:
114 "brew " ++ ruby_str(n)
115 case Br.Cask{n}:
116 "cask " ++ ruby_str(n)
117 case Br.Mas{n, id}:
118 "mas " ++ ruby_str(n) ++ ", id: " ++ id
119 case Br.Other{l}:
120 l
121
122def brewfile(xs: List<&2, Br.Brew>) -> String:
123 match xs:
124 case []:
125 ""
126 case b <> rest:
127 brewfile_line(b) ++ "\n" ++ brewfile(rest)
128
129# ---- reading a Brewfile (as `brew bundle dump` writes it): a line's options after the name are dropped,
130# except a Mac App Store app's id ----
131def digits_of(cs: List<&2, Char>) -> String:
132 match cs:
133 case []:
134 ""
135 case +c <> rest:
136 Bool.pick(String, Char.is_digit(c), String.from_list([c]) ++ digits_of(rest), "")
137
138# The digits after "id:", "" when there are none.
139def mas_id(+l: String) -> String:
140 digits_of(String.to_list(String.trim(Maybe.default(&2, String, List.get(&2, String, String.split(String.drop(l, 4n), ':'), 1n), ""))))
141
142def entry_of(+kind: String, +name: String, +l: String) -> List<&2, Br.Brew>:
143 Bool.pick(List<&2, Br.Brew>, String.is_empty(l) || String.starts_with(l, "#"), [],
144 Bool.pick(List<&2, Br.Brew>, String.eq(kind, "tap"), [Br.Tap{name}],
145 Bool.pick(List<&2, Br.Brew>, String.eq(kind, "brew"), [Br.Formula{name}],
146 Bool.pick(List<&2, Br.Brew>, String.eq(kind, "cask"), [Br.Cask{name}],
147 Bool.pick(List<&2, Br.Brew>, String.eq(kind, "mas"), [Br.Mas{name, mas_id(l)}], [Br.Other{l}])))))
148
149def first_word(+l: String) -> String:
150 Maybe.default(&2, String, List.head(&2, String, String.split(l, ' ')), "")
151
152def quoted_name(+l: String) -> String:
153 Maybe.default(&2, String, List.get(&2, String, String.split(l, '"'), 1n), "")
154
155def of_lines(ls: List<&2, String>) -> List<&2, Br.Brew>:
156 match ls:
157 case []:
158 []
159 case l <> rest:
160 +t = String.trim(l)
161 List.append(&2, Br.Brew, entry_of(first_word(t), quoted_name(t), t), of_lines(rest))
162
163def of_brewfile(text: String) -> List<&2, Br.Brew>:
164 of_lines(String.lines(text))
165
166# ---- Bend source for a list of entries (an imported settings file names the types Br) ----
167def bend_expr(b: Br.Brew) -> String:
168 match b:
169 case Br.Tap{n}:
170 "Br.Tap{" ++ bend_str(n) ++ "}"
171 case Br.Formula{n}:
172 "Br.Formula{" ++ bend_str(n) ++ "}"
173 case Br.Cask{n}:
174 "Br.Cask{" ++ bend_str(n) ++ "}"
175 case Br.Mas{n, id}:
176 "Br.Mas{" ++ bend_str(n) ++ ", " ++ bend_str(id) ++ "}"
177 case Br.Other{l}:
178 "Br.Other{" ++ bend_str(l) ++ "}"
179
180def bend_exprs(xs: List<&2, Br.Brew>) -> List<&2, String>:
181 match xs:
182 case []:
183 []
184 case b <> rest:
185 (" " ++ bend_expr(b)) <> bend_exprs(rest)
186
187def bend_list(xs: List<&2, Br.Brew>) -> String:
188 " [\n" ++ String.join(bend_exprs(xs), ",\n") ++ "\n ]"