diff --git a/lib/cli.nix b/lib/cli.nix new file mode 100644 index 00000000000..f47625d2f53 --- /dev/null +++ b/lib/cli.nix @@ -0,0 +1,56 @@ +{ lib }: + +rec { + /* 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: + encodeGNUCommandLine + { } + { data = builtins.toJSON { id = 0; }; + + X = "PUT"; + + retry = 3; + + retry-delay = null; + + url = [ "https://example.com/foo" "https://example.com/bar" ]; + + silent = false; + + verbose = true; + }; + => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'" + */ + encodeGNUCommandLine = + options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs); + + toGNUCommandLine = + { renderKey ? + key: if builtins.stringLength key == 1 then "-${key}" else "--${key}" + + , renderOption ? + key: value: + if value == null + then [] + else [ (renderKey key) (builtins.toString value) ] + + , renderBool ? key: value: lib.optional value (renderKey key) + + , renderList ? key: value: lib.concatMap (renderOption key) value + }: + options: + let + render = key: value: + if builtins.isBool value + then renderBool key value + else if builtins.isList value + then renderList key value + else renderOption key value; + + in + builtins.concatLists (lib.mapAttrsToList render options); +} diff --git a/lib/default.nix b/lib/default.nix index 77dda17f3b4..5abafe1b2ac 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) encodeGNUCommandLine toGNUCommandLine; 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 b064faa1e1b..e47b48b5017 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -441,4 +441,25 @@ runTests { expected = "«foo»"; }; + testRenderOptions = { + expr = + encodeGNUCommandLine + { } + { data = builtins.toJSON { id = 0; }; + + X = "PUT"; + + retry = 3; + + retry-delay = null; + + url = [ "https://example.com/foo" "https://example.com/bar" ]; + + silent = false; + + verbose = true; + }; + + expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; + }; }