diff --git a/lib/default.nix b/lib/default.nix index 68e5b8dea1eb..f0f136adbc41 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -63,7 +63,7 @@ let inherit (builtins) add addErrorContext attrNames concatLists deepSeq elem elemAt filter genericClosure genList getAttr - hasAttr head isAttrs isBool isInt isList isString length + hasAttr head isAttrs isBool isInt isList isPath isString length lessThan listToAttrs pathExists readFile replaceStrings seq stringLength sub substring tail trace; inherit (self.trivial) id const pipe concat or and bitAnd bitOr bitXor @@ -96,14 +96,16 @@ let concatImapStringsSep makeSearchPath makeSearchPathOutput makeLibraryPath makeBinPath optionalString hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape - escapeShellArg escapeShellArgs isValidPosixName toShellVar toShellVars + escapeShellArg escapeShellArgs + isStorePath isStringLike + isValidPosixName toShellVar toShellVars escapeRegex escapeXML replaceChars lowerChars upperChars toLower toUpper addContextFrom splitString removePrefix removeSuffix versionOlder versionAtLeast getName getVersion mesonOption mesonBool mesonEnable nameFromURL enableFeature enableFeatureAs withFeature - withFeatureAs fixedWidthString fixedWidthNumber isStorePath + withFeatureAs fixedWidthString fixedWidthNumber toInt toIntBase10 readPathsFromFile fileContents; inherit (self.stringsWithDeps) textClosureList textClosureMap noDepEntry fullDepEntry packEntry stringAfter; diff --git a/lib/strings.nix b/lib/strings.nix index 20f6db08cebc..2188fcb1dbfd 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -18,6 +18,7 @@ rec { isInt isList isAttrs + isPath isString match parseDrvName @@ -395,7 +396,7 @@ rec { */ toShellVar = name: value: lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" ( - if isAttrs value && ! isCoercibleToString value then + if isAttrs value && ! isStringLike value then "declare -A ${name}=(${ concatStringsSep " " (lib.mapAttrsToList (n: v: "[${escapeShellArg n}]=${escapeShellArg v}" @@ -798,10 +799,31 @@ rec { in lib.warnIf (!precise) "Imprecise conversion from float to string ${result}" result; - /* Check whether a value can be coerced to a string */ - isCoercibleToString = x: - elem (typeOf x) [ "path" "string" "null" "int" "float" "bool" ] || - (isList x && lib.all isCoercibleToString x) || + /* Soft-deprecated function. While the original implementation is available as + isConvertibleWithToString, consider using isStringLike instead, if suitable. */ + isCoercibleToString = lib.warnIf (lib.isInOldestRelease 2305) + "lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles." + isConvertibleWithToString; + + /* Check whether a list or other value can be passed to toString. + + Many types of value are coercible to string this way, including int, float, + null, bool, list of similarly coercible values. + */ + isConvertibleWithToString = x: + isStringLike x || + elem (typeOf x) [ "null" "int" "float" "bool" ] || + (isList x && lib.all isConvertibleWithToString x); + + /* Check whether a value can be coerced to a string. + The value must be a string, path, or attribute set. + + String-like values can be used without explicit conversion in + string interpolations and in most functions that expect a string. + */ + isStringLike = x: + isString x || + isPath x || x ? outPath || x ? __toString; @@ -818,7 +840,7 @@ rec { => false */ isStorePath = x: - if !(isList x) && isCoercibleToString x then + if isStringLike x then let str = toString x; in substring 0 1 str == "/" && dirOf str == storeDir diff --git a/lib/types.nix b/lib/types.nix index e741bec6c29d..666e6502d161 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -54,7 +54,7 @@ let concatStringsSep escapeNixString hasInfix - isCoercibleToString + isStringLike ; inherit (lib.trivial) boolToString @@ -227,7 +227,7 @@ rec { merge = loc: defs: let getType = value: - if isAttrs value && isCoercibleToString value + if isAttrs value && isStringLike value then "stringCoercibleSet" else builtins.typeOf value; @@ -479,7 +479,7 @@ rec { path = mkOptionType { name = "path"; descriptionClass = "noun"; - check = x: isCoercibleToString x && builtins.substring 0 1 (toString x) == "/"; + check = x: isStringLike x && builtins.substring 0 1 (toString x) == "/"; merge = mergeEqualOption; }; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 10db7cdfb33c..9863cc8e574c 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -42,7 +42,7 @@ let else if isDerivation v then toString v else if builtins.isPath v then toString v else if isString v then v - else if strings.isCoercibleToString v then toString v + else if strings.isConvertibleWithToString v then toString v else abort "The nix conf value: ${toPretty {} v} can not be encoded"; mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}"; diff --git a/nixos/modules/services/system/self-deploy.nix b/nixos/modules/services/system/self-deploy.nix index 9b1ebfd37522..16a793a42253 100644 --- a/nixos/modules/services/system/self-deploy.nix +++ b/nixos/modules/services/system/self-deploy.nix @@ -18,7 +18,7 @@ let in lib.concatStrings (lib.mapAttrsToList toArg args); - isPathType = x: lib.strings.isCoercibleToString x && builtins.substring 0 1 (toString x) == "/"; + isPathType = x: lib.types.path.check x; in {