summary refs log tree commit diff
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2020-01-24 21:05:53 +0100
committerGitHub <noreply@github.com>2020-01-24 21:05:53 +0100
commit07eb21ceaf7a709f3602dd755604b1ce5c53bcdf (patch)
treec5cbd16ce2ebcf9774d1b04cd6f3c2d10870e365
parent5e4c494636078516ab6197f9b65cd47aa36ab289 (diff)
parent7228a3c0bcd80e24a18881b11048d3713042f256 (diff)
downloadnixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar.gz
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar.bz2
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar.lz
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar.xz
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.tar.zst
nixpkgs-07eb21ceaf7a709f3602dd755604b1ce5c53bcdf.zip
Merge pull request #78337 from Profpatsch/lib-improve-cli-module
lib: improve cli module
-rw-r--r--.github/CODEOWNERS2
-rw-r--r--lib/cli.nix93
-rw-r--r--lib/default.nix7
-rw-r--r--lib/generators.nix3
-rw-r--r--lib/tests/misc.nix46
5 files changed, 100 insertions, 51 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 0c06c59f378..f3ac8ad96b7 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -14,7 +14,9 @@
 /lib                        @edolstra @nbp @infinisil
 /lib/systems                @nbp @ericson2314 @matthewbauer
 /lib/generators.nix         @edolstra @nbp @Profpatsch
+/lib/cli.nix                @edolstra @nbp @Profpatsch
 /lib/debug.nix              @edolstra @nbp @Profpatsch
+/lib/asserts.nix            @edolstra @nbp @Profpatsch
 
 # Nixpkgs Internals
 /default.nix                          @nbp
diff --git a/lib/cli.nix b/lib/cli.nix
index f47625d2f53..c96d4dbb043 100644
--- a/lib/cli.nix
+++ b/lib/cli.nix
@@ -6,50 +6,77 @@ rec {
      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;
+     `toGNUCommandLine` returns a list of nix strings.
+     `toGNUCommandLineShell` returns an escaped shell string.
 
-           retry-delay = null;
-
-           url = [ "https://example.com/foo" "https://example.com/bar" ];
-
-           silent = false;
+     Example:
+       cli.toGNUCommandLine {} {
+         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"
+       ]
 
-           verbose = true;
-         };
-       => "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"
+       cli.toGNUCommandLineShell {} {
+         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 =
+  toGNUCommandLineShell =
     options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
 
-  toGNUCommandLine =
-    { renderKey ?
-        key: if builtins.stringLength key == 1 then "-${key}" else "--${key}"
+  toGNUCommandLine = {
+    # how to string-format the option name;
+    # by default one character is a short option (`-`),
+    # more than one characters a long option (`--`).
+    mkOptionName ?
+      k: if builtins.stringLength k == 1
+          then "-${k}"
+          else "--${k}",
 
-    , renderOption ?
-        key: value:
-          if value == null
-          then []
-          else [ (renderKey key) (builtins.toString value) ]
+    # how to format a boolean value to a command list;
+    # by default it’s a flag option
+    # (only the option name if true, left out completely if false).
+    mkBool ? k: v: lib.optional v (mkOptionName k),
 
-    , renderBool ? key: value: lib.optional value (renderKey key)
+    # how to format a list value to a command list;
+    # by default the option name is repeated for each value
+    # and `mkOption` is applied to the values themselves.
+    mkList ? k: v: lib.concatMap (mkOption k) v,
 
-    , renderList ? key: value: lib.concatMap (renderOption key) value
+    # how to format any remaining value to a command list;
+    # on the toplevel, booleans and lists are handled by `mkBool` and `mkList`,
+    # though they can still appear as values of a list.
+    # By default, everything is printed verbatim and complex types
+    # are forbidden (lists, attrsets, functions). `null` values are omitted.
+    mkOption ?
+      k: v: if v == null
+            then []
+            else [ (mkOptionName k) (lib.generators.mkValueStringDefault {} v) ]
     }:
     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;
+        render = k: v:
+          if      builtins.isBool v then mkBool k v
+          else if builtins.isList v then mkList k v
+          else mkOption k v;
 
       in
         builtins.concatLists (lib.mapAttrsToList render options);
diff --git a/lib/default.nix b/lib/default.nix
index d2e9f0e8086..d2fe018aa6a 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -37,11 +37,13 @@ let
     licenses = callLibs ./licenses.nix;
     systems = callLibs ./systems;
 
+    # serialization
+    cli = callLibs ./cli.nix;
+    generators = callLibs ./generators.nix;
+
     # misc
     asserts = callLibs ./asserts.nix;
-    cli = callLibs ./cli.nix;
     debug = callLibs ./debug.nix;
-    generators = callLibs ./generators.nix;
     misc = callLibs ./deprecated.nix;
 
     # domain-specific
@@ -121,7 +123,6 @@ 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/generators.nix b/lib/generators.nix
index a71654bec6c..a64e94bd5cb 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -46,7 +46,10 @@ rec {
     else if isList     v then err "lists" v
     # same as for lists, might want to replace
     else if isAttrs    v then err "attrsets" v
+    # functions can’t be printed of course
     else if isFunction v then err "functions" v
+    # let’s not talk about floats. There is no sensible `toString` for them.
+    else if isFloat    v then err "floats" v
     else err "this value is" (toString v);
 
 
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index e47b48b5017..59ed1e507e2 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -441,24 +441,40 @@ 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" ];
+# CLI
+
+  testToGNUCommandLine = {
+    expr = cli.toGNUCommandLine {} {
+      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;
+    };
 
-           silent = false;
+    expected = [
+      "-X" "PUT"
+      "--data" "{\"id\":0}"
+      "--retry" "3"
+      "--url" "https://example.com/foo"
+      "--url" "https://example.com/bar"
+      "--verbose"
+    ];
+  };
 
-           verbose = true;
-         };
+  testToGNUCommandLineShell = {
+    expr = cli.toGNUCommandLineShell {} {
+      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'";
   };