From 3d196a5f2a72595b14c439a9b4aba7737c0f1ebe Mon Sep 17 00:00:00 2001 From: Jacob Abel Date: Mon, 23 May 2022 03:39:48 -0400 Subject: lib/strings: Update toInt to handle intermixed ws and zeros. Added tests --- lib/strings.nix | 20 +++++++++++++------- lib/tests/misc.nix | 11 +++++++++++ lib/tests/modules.sh | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index 8f3568fc1fc..c6269e755e2 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -802,16 +802,22 @@ rec { # Obviously, it is a bit hacky to use fromJSON this way. toInt = str: let - strippedInput = match "[[:space:]]*(0*)(.*)" str; - isNonZeroEmpty = match "[[:space:]]*" (lib.last strippedInput) == []; - isZeroNonEmpty = head strippedInput != ""; - mayBeInt = fromJSON (lib.last strippedInput); + # RegEx: Match any leading whitespace, then any zero padding, and capture any remaining + # digits after that, and finally match any trailing whitespace. + strippedInput = match "[[:space:]]*0*([[:digit:]]+)[[:space:]]*" str; + + # RegEx: Match any leading whitespace, at least one '0', and any trailing whitespace. + isZero = match "[[:space:]]*0+[[:space:]]*" str == []; + + # Attempt to parse input + parsedInput = fromJSON (elemAt strippedInput 0); in - if isNonZeroEmpty && isZeroNonEmpty + # Value is zero + if isZero then 0 else - if isInt mayBeInt - then mayBeInt + if strippedInput != null && isInt parsedInput + then parsedInput else throw "Could not convert ${str} to int."; /* Read a list of paths from `file`, relative to the `rootPath`. diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index ef4483219f7..97d53026c64 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -355,6 +355,17 @@ runTests { (0 == toInt " 000000 ") ]; + testToIntFails = testAllTrue [ + ( builtins.tryEval (toInt "") == { success = false; value = false; } ) + ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } ) + ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } ) + ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } ) + ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } ) + ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } ) + ( builtins.tryEval (toInt " foo 00123 ") == { success = false; value = false; } ) + ( builtins.tryEval (toInt " foo00123 ") == { success = false; value = false; } ) + ]; + # LISTS testFilter = { diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh index 2be9b583509..f6298297d13 100755 --- a/lib/tests/modules.sh +++ b/lib/tests/modules.sh @@ -162,7 +162,7 @@ checkConfigError 'A definition for option .* is not.*string or signed integer co # Check coerced value with unsound coercion checkConfigOutput '^12$' config.value ./declare-coerced-value-unsound.nix checkConfigError 'A definition for option .* is not of type .*. Definition values:\n\s*- In .*: "1000"' config.value ./declare-coerced-value-unsound.nix ./define-value-string-bigint.nix -checkConfigError 'json.exception.parse_error' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix +checkConfigError 'Could not convert .* to int.' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix # Check mkAliasOptionModule. checkConfigOutput '^true$' config.enable ./alias-with-priority.nix -- cgit 1.4.1