diff --git a/lib/cli.nix b/lib/cli.nix new file mode 100644 index 000000000000..d794778b21a4 --- /dev/null +++ b/lib/cli.nix @@ -0,0 +1,33 @@ +{ lib }: + +{ /* Automatically convert an attribute set to command-line options. + + This helps protect against malformed command lines and also to reduce + boilerplate related to command-line construction for simple use cases. + + Example: + renderOptions { foo = "A"; bar = 1; baz = null; qux = true; v = true; } + => " --bar '1' --foo 'A' --qux -v" + */ + renderOptions = + options: + let + render = key: value: + let + hyphenate = + k: if builtins.stringLength k == 1 then "-${k}" else "--${k}"; + + renderOption = v: if v == null then "" else " ${hyphenate key} ${lib.escapeShellArg v}"; + + renderSwitch = if value then " ${hyphenate key}" else ""; + + in + if builtins.isBool value + then renderSwitch + else if builtins.isList value + then lib.concatMapStrings renderOption value + else renderOption value; + + in + lib.concatStrings (lib.mapAttrsToList render options); +} diff --git a/lib/default.nix b/lib/default.nix index 8af531525860..5798c6bba007 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -39,6 +39,7 @@ let # misc asserts = callLibs ./asserts.nix; + cli = callLibs ./cli.nix; debug = callLibs ./debug.nix; generators = callLibs ./generators.nix; misc = callLibs ./deprecated.nix; @@ -120,6 +121,7 @@ let isOptionType mkOptionType; inherit (asserts) assertMsg assertOneOf; + inherit (cli) renderOptions; inherit (debug) addErrorContextToAttrs traceIf traceVal traceValFn traceXMLVal traceXMLValMarked traceSeq traceSeqN traceValSeq traceValSeqFn traceValSeqN traceValSeqNFn traceShowVal diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index b064faa1e1ba..a5f191410e54 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -441,4 +441,20 @@ runTests { expected = "«foo»"; }; + testRenderOptions = { + expr = + renderOptions + { foo = "A"; + + bar = 1; + + baz = null; + + qux = true; + + v = true; + }; + + expected = " --bar '1' --foo 'A' --qux -v"; + }; }