summary refs log tree commit diff
diff options
context:
space:
mode:
authorNaïm Favier <n@monade.li>2022-04-27 10:02:49 +0200
committerNaïm Favier <n@monade.li>2022-04-27 16:04:17 +0200
commit226bc996597407aed9fced101234962a542d6bfd (patch)
tree66eb659558d367195a58c955e0df286679b47e40
parent68322e1297ce606a37622186c85c7b60dd336a1c (diff)
downloadnixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar.gz
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar.bz2
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar.lz
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar.xz
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.tar.zst
nixpkgs-226bc996597407aed9fced101234962a542d6bfd.zip
lib/strings: add toShellVars
A straightforward piece of plumbing to safely inject Nix variables into
shell scripts:

''
  ${lib.toShellVars { inherit foo bar; }}
  cmd "$foo" --bar "$bar"
''
-rw-r--r--lib/default.nix3
-rw-r--r--lib/strings.nix60
-rw-r--r--lib/tests/misc.nix20
3 files changed, 82 insertions, 1 deletions
diff --git a/lib/default.nix b/lib/default.nix
index 1f06283790a..6327c8518ff 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -94,7 +94,8 @@ let
       concatImapStringsSep makeSearchPath makeSearchPathOutput
       makeLibraryPath makeBinPath optionalString
       hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
-      escapeShellArg escapeShellArgs escapeRegex escapeXML replaceChars lowerChars
+      escapeShellArg escapeShellArgs isValidPosixName toShellVar toShellVars
+      escapeRegex escapeXML replaceChars lowerChars
       upperChars toLower toUpper addContextFrom splitString
       removePrefix removeSuffix versionOlder versionAtLeast
       getName getVersion
diff --git a/lib/strings.nix b/lib/strings.nix
index 11066890ec3..bad8f53fa56 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -17,6 +17,7 @@ rec {
     head
     isInt
     isList
+    isAttrs
     isString
     match
     parseDrvName
@@ -324,6 +325,65 @@ rec {
   */
   escapeShellArgs = concatMapStringsSep " " escapeShellArg;
 
+  /* Test whether the given name is a valid POSIX shell variable name.
+
+     Type: string -> bool
+
+     Example:
+       isValidPosixName "foo_bar000"
+       => true
+       isValidPosixName "0-bad.jpg"
+       => false
+  */
+  isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
+
+  /* Translate a Nix value into a shell variable declaration, with proper escaping.
+
+     Supported value types are strings (mapped to regular variables), lists of strings
+     (mapped to Bash-style arrays) and attribute sets of strings (mapped to Bash-style
+     associative arrays). Note that "strings" include string-coercible values like paths.
+
+     Strings are translated into POSIX sh-compatible code; lists and attribute sets
+     assume a shell that understands Bash syntax (e.g. Bash or ZSH).
+
+     Type: string -> (string | listOf string | attrsOf string) -> string
+
+     Example:
+       ''
+         ${toShellVar foo "some string"}
+         [[ "$foo" == "some string" ]]
+       ''
+  */
+  toShellVar = name: value:
+    lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
+    if isAttrs value then
+      "declare -A ${name}=(${
+        concatStringsSep " " (lib.mapAttrsToList (n: v:
+          "[${escapeShellArg n}]=${escapeShellArg v}"
+        ) value)
+      })"
+    else if isList value then
+      "declare -a ${name}=(${escapeShellArgs value})"
+    else
+      "${name}=${escapeShellArg value}"
+    );
+
+  /* Translate an attribute set into corresponding shell variable declarations
+     using `toShellVar`.
+
+     Type: attrsOf (string | listOf string | attrsOf string) -> string
+
+     Example:
+       let
+         foo = "value";
+         bar = foo;
+       in ''
+         ${toShellVars { inherit foo bar; }}
+         [[ "$foo" == "$bar" ]]
+       ''
+  */
+  toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
+
   /* Turn a string into a Nix expression representing that string
 
      Type: string -> string
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index fcccf89cc88..c5d1d431677 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -251,6 +251,26 @@ runTests {
     expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;";
   };
 
+  testToShellVars = {
+    expr = ''
+      ${toShellVars {
+        STRing01 = "just a 'string'";
+        _array_ = [ "with" "more strings" ];
+        assoc."with some" = ''
+          strings
+          possibly newlines
+        '';
+      }}
+    '';
+    expected = ''
+      STRing01='just a '\'''string'\''''
+      declare -a _array_=('with' 'more strings')
+      declare -A assoc=(['with some']='strings
+      possibly newlines
+      ')
+    '';
+  };
+
 # LISTS
 
   testFilter = {