Unverified Commit 43b77e14 authored by Johannes Kirschbauer's avatar Johannes Kirschbauer Committed by GitHub
Browse files

lib/cli: add toCommandLine (#404233)

parents 486367a7 1f4c50ab
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -359,6 +359,8 @@
  - `number`
  - `numbers.*`

- `lib.cli.toGNUCommandLine` and `lib.cli.toGNUCommandLineShell` have been deprecated in favor of `lib.cli.toCommandLine`, `lib.cli.toCommandLineShell`, `lib.cli.toCommandLineGNU` and `lib.cli.toCommandLineShellGNU`.

### Additions and Improvements {#sec-nixpkgs-release-25.11-lib-additions-improvements}

- `neovim`: Added support for the `vim.o.exrc` option, the `VIMINIT` environment variable, and sourcing of `sysinit.vim`.
@@ -366,3 +368,5 @@
  See the neovim help page [`:help startup`](https://neovim.io/doc/user/starting.html#startup) for more information, as well as [the nixpkgs neovim wrapper documentation](#neovim-custom-configuration).

- `cloudflare-ddns`: Added package cloudflare-ddns.

- `lib.cli.toCommandLine`, `lib.cli.toCommandLineShell`, `lib.cli.toCommandLineGNU` and `lib.cli.toCommandLineShellGNU` have been added to address multiple issues in `lib.cli.toGNUCommandLine` and `lib.cli.toGNUCommandLineShell`.
+217 −26
Original line number Diff line number Diff line
{ lib }:

rec {
{
  /**
    Automatically convert an attribute set to command-line options.

@@ -20,6 +20,7 @@ rec {
    : The attributes to transform into arguments.

    # Examples

    :::{.example}
    ## `lib.cli.toGNUCommandLineShell` usage example

@@ -38,7 +39,10 @@ rec {

    :::
  */
  toGNUCommandLineShell = options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
  toGNUCommandLineShell =
    lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
      "lib.cli.toGNUCommandLineShell is deprecated, please use lib.cli.toCommandLineShell or lib.cli.toCommandLineShellGNU instead."
      (options: attrs: lib.escapeShellArgs (lib.cli.toGNUCommandLine options attrs));

  /**
    Automatically convert an attribute set to a list of command-line options.
@@ -55,7 +59,7 @@ rec {

    : The attributes to transform into arguments.

    # Options
    ## Options

    `mkOptionName`

@@ -85,6 +89,7 @@ rec {
    This is useful if the command requires equals, for example, `-c=5`.

    # Examples

    :::{.example}
    ## `lib.cli.toGNUCommandLine` usage example

@@ -111,6 +116,9 @@ rec {
    :::
  */
  toGNUCommandLine =
    lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
      "lib.cli.toGNUCommandLine is deprecated, please use lib.cli.toCommandLine or lib.cli.toCommandLineShellGNU instead."
      (
        {
          mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}",

@@ -144,5 +152,188 @@ rec {
              mkOption k v;

        in
    builtins.concatLists (lib.mapAttrsToList render options);
        builtins.concatLists (lib.mapAttrsToList render options)
      );

  /**
    Converts the given attributes into a single shell-escaped command-line string.
    Similar to `toCommandLineGNU`, but returns a single escaped string instead of an array of arguments.
    For further reference see: [`lib.cli.toCommandLineGNU`](#function-library-lib.cli.toCommandLineGNU)
  */
  toCommandLineShellGNU =
    options: attrs: lib.escapeShellArgs (lib.cli.toCommandLineGNU options attrs);

  /**
    Converts an attribute set into a list of GNU-style command line options.

    `toCommandLineGNU` returns a list of string arguments.

    # Inputs

    `options`

    : Options, see below.

    `attrs`

    : The attributes to transform into arguments.

    ## Options

    `isLong`

    : A function that determines whether an option is long or short.

    `explicitBool`

    : Whether or not boolean option arguments should be formatted explicitly.

    `formatArg`

    : A function that turns the option argument into a string.

    # Examples

    :::{.example}
    ## `lib.cli.toCommandLineGNU` usage example

    ```nix
    lib.cli.toCommandLineGNU {} {
      v = true;
      verbose = [true true false null];
      i = ".bak";
      testsuite = ["unit" "integration"];
      e = ["s/a/b/" "s/b/c/"];
      n = false;
      data = builtins.toJSON {id = 0;};
    }
    => [
      "--data={\"id\":0}"
      "-es/a/b/"
      "-es/b/c/"
      "-i.bak"
      "--testsuite=unit"
      "--testsuite=integration"
      "-v"
      "--verbose"
      "--verbose"
    ]
    ```

    :::
  */
  toCommandLineGNU =
    {
      isLong ? optionName: builtins.stringLength optionName > 1,
      explicitBool ? false,
      formatArg ? lib.generators.mkValueStringDefault { },
    }:
    let
      optionFormat = optionName: {
        option = if isLong optionName then "--${optionName}" else "-${optionName}";
        sep = if isLong optionName then "=" else "";
        inherit explicitBool formatArg;
      };
    in
    lib.cli.toCommandLine optionFormat;

  /**
    Converts the given attributes into a single shell-escaped command-line string.
    Similar to `toCommandLine`, but returns a single escaped string instead of an array of arguments.
    For further reference see: [`lib.cli.toCommandLine`](#function-library-lib.cli.toCommandLine)
  */
  toCommandLineShell =
    optionFormat: attrs: lib.escapeShellArgs (lib.cli.toCommandLine optionFormat attrs);

  /**
    Converts an attribute set into a list of command line options.

    `toCommandLine` returns a list of string arguments.

    # Inputs

    `optionFormat`

    : The option format that describes how options and their arguments should be formatted.

    `attrs`

    : The attributes to transform into arguments.

    # Examples
    :::{.example}
    ## `lib.cli.toCommandLine` usage example

    ```nix
    let
      optionFormat = optionName: {
        option = "-${optionName}";
        sep = "=";
        explicitBool = true;
      };
    in lib.cli.toCommandLine optionFormat {
      v = true;
      verbose = [true true false null];
      i = ".bak";
      testsuite = ["unit" "integration"];
      e = ["s/a/b/" "s/b/c/"];
      n = false;
      data = builtins.toJSON {id = 0;};
    }
    => [
      "-data={\"id\":0}"
      "-e=s/a/b/"
      "-e=s/b/c/"
      "-i=.bak"
      "-n=false"
      "-testsuite=unit"
      "-testsuite=integration"
      "-v=true"
      "-verbose=true"
      "-verbose=true"
      "-verbose=false"
    ]
    ```

    :::
  */
  toCommandLine =
    optionFormat: attrs:
    let
      handlePair =
        k: v:
        if k == "" then
          lib.throw "lib.cli.toCommandLine only accepts non-empty option names."
        else if builtins.isList v then
          builtins.concatMap (handleOption k) v
        else
          handleOption k v;

      handleOption = k: renderOption (optionFormat k) k;

      renderOption =
        {
          option,
          sep,
          explicitBool,
          formatArg ? lib.generators.mkValueStringDefault { },
        }:
        k: v:
        if v == null || (!explicitBool && v == false) then
          [ ]
        else if !explicitBool && v == true then
          [ option ]
        else
          let
            arg = formatArg v;
          in
          if sep != null then
            [ "${option}${sep}${arg}" ]
          else
            [
              option
              arg
            ];
    in
    builtins.concatLists (lib.mapAttrsToList handlePair attrs);
}
+80 −0
Original line number Diff line number Diff line
@@ -3106,6 +3106,86 @@ runTests {
    expected = "-X PUT --data '{\"id\":0}' --retry 3 --url https://example.com/foo --url https://example.com/bar --verbose";
  };

  testToCommandLine = {
    expr =
      let
        optionFormat = optionName: {
          option = "-${optionName}";
          sep = "=";
          explicitBool = true;
        };
      in
      cli.toCommandLine optionFormat {
        v = true;
        verbose = [
          true
          true
          false
          null
        ];
        i = ".bak";
        testsuite = [
          "unit"
          "integration"
        ];
        e = [
          "s/a/b/"
          "s/b/c/"
        ];
        n = false;
        data = builtins.toJSON { id = 0; };
      };

    expected = [
      "-data={\"id\":0}"
      "-e=s/a/b/"
      "-e=s/b/c/"
      "-i=.bak"
      "-n=false"
      "-testsuite=unit"
      "-testsuite=integration"
      "-v=true"
      "-verbose=true"
      "-verbose=true"
      "-verbose=false"
    ];
  };

  testToCommandLineGNU = {
    expr = cli.toCommandLineGNU { } {
      v = true;
      verbose = [
        true
        true
        false
        null
      ];
      i = ".bak";
      testsuite = [
        "unit"
        "integration"
      ];
      e = [
        "s/a/b/"
        "s/b/c/"
      ];
      n = false;
      data = builtins.toJSON { id = 0; };
    };

    expected = [
      "--data={\"id\":0}"
      "-es/a/b/"
      "-es/b/c/"
      "-i.bak"
      "--testsuite=unit"
      "--testsuite=integration"
      "-v"
      "--verbose"
      "--verbose"
    ];
  };

  testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
    name = "..foo";
    expected = "foo";
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ let
  ensurePrinter =
    p:
    let
      args = lib.cli.toGNUCommandLineShell { } (
      args = lib.cli.toCommandLineShellGNU { } (
        {
          p = p.name;
          v = p.deviceUri;
+3 −3
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ libeufinComponent:
            DynamicUser = true;
            ExecStart =
              let
                args = lib.cli.toGNUCommandLineShell { } {
                args = lib.cli.toCommandLineShellGNU { } {
                  c = configFile;
                  L = if cfg.debug then "debug" else null;
                };
@@ -80,7 +80,7 @@ libeufinComponent:
            initialAccountRegistration = lib.concatMapStringsSep "\n" (
              account:
              let
                args = lib.cli.toGNUCommandLineShell { } {
                args = lib.cli.toCommandLineShellGNU { } {
                  c = configFile;
                  inherit (account) username password name;
                  payto_uri = "payto://x-taler-bank/${bankHost}/${account.username}?receiver-name=${account.name}";
@@ -90,7 +90,7 @@ libeufinComponent:
              "${lib.getExe' cfg.package "libeufin-bank"} create-account ${args}"
            ) cfg.initialAccounts;

            args = lib.cli.toGNUCommandLineShell { } {
            args = lib.cli.toCommandLineShellGNU { } {
              c = configFile;
              L = if cfg.debug then "debug" else null;
            };
Loading