1{ lib }:
2
3rec {
4 /* Automatically convert an attribute set to command-line options.
5
6 This helps protect against malformed command lines and also to reduce
7 boilerplate related to command-line construction for simple use cases.
8
9 `toGNUCommandLine` returns a list of nix strings.
10 `toGNUCommandLineShell` returns an escaped shell string.
11
12 Example:
13 cli.toGNUCommandLine {} {
14 data = builtins.toJSON { id = 0; };
15 X = "PUT";
16 retry = 3;
17 retry-delay = null;
18 url = [ "https://example.com/foo" "https://example.com/bar" ];
19 silent = false;
20 verbose = true;
21 }
22 => [
23 "-X" "PUT"
24 "--data" "{\"id\":0}"
25 "--retry" "3"
26 "--url" "https://example.com/foo"
27 "--url" "https://example.com/bar"
28 "--verbose"
29 ]
30
31 cli.toGNUCommandLineShell {} {
32 data = builtins.toJSON { id = 0; };
33 X = "PUT";
34 retry = 3;
35 retry-delay = null;
36 url = [ "https://example.com/foo" "https://example.com/bar" ];
37 silent = false;
38 verbose = true;
39 }
40 => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
41 */
42 toGNUCommandLineShell =
43 options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
44
45 toGNUCommandLine = {
46 # how to string-format the option name;
47 # by default one character is a short option (`-`),
48 # more than one characters a long option (`--`).
49 mkOptionName ?
50 k: if builtins.stringLength k == 1
51 then "-${k}"
52 else "--${k}",
53
54 # how to format a boolean value to a command list;
55 # by default it’s a flag option
56 # (only the option name if true, left out completely if false).
57 mkBool ? k: v: lib.optional v (mkOptionName k),
58
59 # how to format a list value to a command list;
60 # by default the option name is repeated for each value
61 # and `mkOption` is applied to the values themselves.
62 mkList ? k: v: lib.concatMap (mkOption k) v,
63
64 # how to format any remaining value to a command list;
65 # on the toplevel, booleans and lists are handled by `mkBool` and `mkList`,
66 # though they can still appear as values of a list.
67 # By default, everything is printed verbatim and complex types
68 # are forbidden (lists, attrsets, functions). `null` values are omitted.
69 mkOption ?
70 k: v: if v == null
71 then []
72 else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ]
73 }:
74 options:
75 let
76 render = k: v:
77 if builtins.isBool v then mkBool k v
78 else if builtins.isList v then mkList k v
79 else mkOption k v;
80
81 in
82 builtins.concatLists (lib.mapAttrsToList render options);
83}