nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{ lib }:
2
3{
4 /**
5 Automatically convert an attribute set to command-line options.
6
7 This helps protect against malformed command lines and also to reduce
8 boilerplate related to command-line construction for simple use cases.
9
10 `toGNUCommandLineShell` returns an escaped shell string.
11
12 # Inputs
13
14 `options`
15
16 : How to format the arguments, see `toGNUCommandLine`
17
18 `attrs`
19
20 : The attributes to transform into arguments.
21
22 # Examples
23
24 :::{.example}
25 ## `lib.cli.toGNUCommandLineShell` usage example
26
27 ```nix
28 cli.toGNUCommandLineShell {} {
29 data = builtins.toJSON { id = 0; };
30 X = "PUT";
31 retry = 3;
32 retry-delay = null;
33 url = [ "https://example.com/foo" "https://example.com/bar" ];
34 silent = false;
35 verbose = true;
36 }
37 => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
38 ```
39
40 :::
41 */
42 toGNUCommandLineShell =
43 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
44 "lib.cli.toGNUCommandLineShell is deprecated, please use lib.cli.toCommandLineShell or lib.cli.toCommandLineShellGNU instead."
45 (options: attrs: lib.escapeShellArgs (lib.cli.toGNUCommandLine options attrs));
46
47 /**
48 Automatically convert an attribute set to a list of command-line options.
49
50 `toGNUCommandLine` returns a list of string arguments.
51
52 # Inputs
53
54 `options`
55
56 : How to format the arguments, see below.
57
58 `attrs`
59
60 : The attributes to transform into arguments.
61
62 ## Options
63
64 `mkOptionName`
65
66 : How to string-format the option name;
67 By default one character is a short option (`-`), more than one characters a long option (`--`).
68
69 `mkBool`
70
71 : How to format a boolean value to a command list;
72 By default it’s a flag option (only the option name if true, left out completely if false).
73
74 `mkList`
75
76 : How to format a list value to a command list;
77 By default the option name is repeated for each value and `mkOption` is applied to the values themselves.
78
79 `mkOption`
80
81 : How to format any remaining value to a command list;
82 On the toplevel, booleans and lists are handled by `mkBool` and `mkList`, though they can still appear as values of a list.
83 By default, everything is printed verbatim and complex types are forbidden (lists, attrsets, functions). `null` values are omitted.
84
85 `optionValueSeparator`
86
87 : How to separate an option from its flag;
88 By default, there is no separator, so option `-c` and value `5` would become ["-c" "5"].
89 This is useful if the command requires equals, for example, `-c=5`.
90
91 # Examples
92
93 :::{.example}
94 ## `lib.cli.toGNUCommandLine` usage example
95
96 ```nix
97 cli.toGNUCommandLine {} {
98 data = builtins.toJSON { id = 0; };
99 X = "PUT";
100 retry = 3;
101 retry-delay = null;
102 url = [ "https://example.com/foo" "https://example.com/bar" ];
103 silent = false;
104 verbose = true;
105 }
106 => [
107 "-X" "PUT"
108 "--data" "{\"id\":0}"
109 "--retry" "3"
110 "--url" "https://example.com/foo"
111 "--url" "https://example.com/bar"
112 "--verbose"
113 ]
114 ```
115
116 :::
117 */
118 toGNUCommandLine =
119 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
120 "lib.cli.toGNUCommandLine is deprecated, please use lib.cli.toCommandLine or lib.cli.toCommandLineShellGNU instead."
121 (
122 {
123 mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}",
124
125 mkBool ? k: v: lib.optional v (mkOptionName k),
126
127 mkList ? k: v: lib.concatMap (mkOption k) v,
128
129 mkOption ?
130 k: v:
131 if v == null then
132 [ ]
133 else if optionValueSeparator == null then
134 [
135 (mkOptionName k)
136 (lib.generators.mkValueStringDefault { } v)
137 ]
138 else
139 [ "${mkOptionName k}${optionValueSeparator}${lib.generators.mkValueStringDefault { } v}" ],
140
141 optionValueSeparator ? null,
142 }:
143 options:
144 let
145 render =
146 k: v:
147 if builtins.isBool v then
148 mkBool k v
149 else if builtins.isList v then
150 mkList k v
151 else
152 mkOption k v;
153
154 in
155 builtins.concatLists (lib.mapAttrsToList render options)
156 );
157
158 /**
159 Converts the given attributes into a single shell-escaped command-line string.
160 Similar to `toCommandLineGNU`, but returns a single escaped string instead of an array of arguments.
161 For further reference see: [`lib.cli.toCommandLineGNU`](#function-library-lib.cli.toCommandLineGNU)
162 */
163 toCommandLineShellGNU =
164 options: attrs: lib.escapeShellArgs (lib.cli.toCommandLineGNU options attrs);
165
166 /**
167 Converts an attribute set into a list of GNU-style command line options.
168
169 `toCommandLineGNU` returns a list of string arguments.
170
171 # Inputs
172
173 `options`
174
175 : Options, see below.
176
177 `attrs`
178
179 : The attributes to transform into arguments.
180
181 ## Options
182
183 `isLong`
184
185 : A function that determines whether an option is long or short.
186
187 `explicitBool`
188
189 : Whether or not boolean option arguments should be formatted explicitly.
190
191 `formatArg`
192
193 : A function that turns the option argument into a string.
194
195 # Examples
196
197 :::{.example}
198 ## `lib.cli.toCommandLineGNU` usage example
199
200 ```nix
201 lib.cli.toCommandLineGNU {} {
202 v = true;
203 verbose = [true true false null];
204 i = ".bak";
205 testsuite = ["unit" "integration"];
206 e = ["s/a/b/" "s/b/c/"];
207 n = false;
208 data = builtins.toJSON {id = 0;};
209 }
210 => [
211 "--data={\"id\":0}"
212 "-es/a/b/"
213 "-es/b/c/"
214 "-i.bak"
215 "--testsuite=unit"
216 "--testsuite=integration"
217 "-v"
218 "--verbose"
219 "--verbose"
220 ]
221 ```
222
223 :::
224 */
225 toCommandLineGNU =
226 {
227 isLong ? optionName: builtins.stringLength optionName > 1,
228 explicitBool ? false,
229 formatArg ? lib.generators.mkValueStringDefault { },
230 }:
231 let
232 optionFormat = optionName: {
233 option = if isLong optionName then "--${optionName}" else "-${optionName}";
234 sep = if isLong optionName then "=" else "";
235 inherit explicitBool formatArg;
236 };
237 in
238 lib.cli.toCommandLine optionFormat;
239
240 /**
241 Converts the given attributes into a single shell-escaped command-line string.
242 Similar to `toCommandLine`, but returns a single escaped string instead of an array of arguments.
243 For further reference see: [`lib.cli.toCommandLine`](#function-library-lib.cli.toCommandLine)
244 */
245 toCommandLineShell =
246 optionFormat: attrs: lib.escapeShellArgs (lib.cli.toCommandLine optionFormat attrs);
247
248 /**
249 Converts an attribute set into a list of command line options.
250
251 `toCommandLine` returns a list of string arguments.
252
253 # Inputs
254
255 `optionFormat`
256
257 : The option format that describes how options and their arguments should be formatted.
258
259 `attrs`
260
261 : The attributes to transform into arguments.
262
263 # Examples
264 :::{.example}
265 ## `lib.cli.toCommandLine` usage example
266
267 ```nix
268 let
269 optionFormat = optionName: {
270 option = "-${optionName}";
271 sep = "=";
272 explicitBool = true;
273 };
274 in lib.cli.toCommandLine optionFormat {
275 v = true;
276 verbose = [true true false null];
277 i = ".bak";
278 testsuite = ["unit" "integration"];
279 e = ["s/a/b/" "s/b/c/"];
280 n = false;
281 data = builtins.toJSON {id = 0;};
282 }
283 => [
284 "-data={\"id\":0}"
285 "-e=s/a/b/"
286 "-e=s/b/c/"
287 "-i=.bak"
288 "-n=false"
289 "-testsuite=unit"
290 "-testsuite=integration"
291 "-v=true"
292 "-verbose=true"
293 "-verbose=true"
294 "-verbose=false"
295 ]
296 ```
297
298 :::
299 */
300 toCommandLine =
301 optionFormat: attrs:
302 let
303 handlePair =
304 k: v:
305 if k == "" then
306 lib.throw "lib.cli.toCommandLine only accepts non-empty option names."
307 else if builtins.isList v then
308 builtins.concatMap (handleOption k) v
309 else
310 handleOption k v;
311
312 handleOption = k: renderOption (optionFormat k) k;
313
314 renderOption =
315 {
316 option,
317 sep,
318 explicitBool,
319 formatArg ? lib.generators.mkValueStringDefault { },
320 }:
321 k: v:
322 if v == null || (!explicitBool && v == false) then
323 [ ]
324 else if !explicitBool && v == true then
325 [ option ]
326 else
327 let
328 arg = formatArg v;
329 in
330 if sep != null then
331 [ "${option}${sep}${arg}" ]
332 else
333 [
334 option
335 arg
336 ];
337 in
338 builtins.concatLists (lib.mapAttrsToList handlePair attrs);
339}