about summary refs log tree commit diff
path: root/lib/strings.nix
diff options
context:
space:
mode:
authorRobert Hensing <roberth@users.noreply.github.com>2022-10-24 13:22:42 +0200
committerGitHub <noreply@github.com>2022-10-24 13:22:42 +0200
commitbc4ce318bf6aa52031ad891675c573ce31308c8d (patch)
treefde3afb99befe8b7cfa46ae90cab9536bf19cdf6 /lib/strings.nix
parentdf9da891637929d52a8a189ea3399c1ee555ef51 (diff)
parented71173841618bd4c69f40d07fb467ccabc5db0b (diff)
Merge pull request #173949 from jacoblambda/fix-toInt-zero-padding
lib: add strings.toIntBase10 to parse zero-padded strings
Diffstat (limited to 'lib/strings.nix')
-rw-r--r--lib/strings.nix93
1 files changed, 87 insertions, 6 deletions
diff --git a/lib/strings.nix b/lib/strings.nix
index af26532aa4305..b5f5a4d9060ba 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -783,24 +783,105 @@ rec {
     else
       false;
 
-  /* Parse a string as an int.
+  /* Parse a string as an int. Does not support parsing of integers with preceding zero due to
+  ambiguity between zero-padded and octal numbers. See toIntBase10.
 
      Type: string -> int
 
      Example:
+
        toInt "1337"
        => 1337
+
        toInt "-4"
        => -4
+
+       toInt " 123 "
+       => 123
+
+       toInt "00024"
+       => error: Ambiguity in interpretation of 00024 between octal and zero padded integer.
+
        toInt "3.14"
        => error: floating point JSON numbers are not supported
   */
-  # Obviously, it is a bit hacky to use fromJSON this way.
   toInt = str:
-    let may_be_int = fromJSON str; in
-    if isInt may_be_int
-    then may_be_int
-    else throw "Could not convert ${str} to int.";
+    let
+      # RegEx: Match any leading whitespace, then any digits, and finally match any trailing
+      # whitespace.
+      strippedInput = match "[[:space:]]*([[:digit:]]+)[[:space:]]*" str;
+
+      # RegEx: Match a leading '0' then one or more digits.
+      isLeadingZero = match "0[[:digit:]]+" (head strippedInput) == [];
+
+      # Attempt to parse input
+      parsedInput = fromJSON (head strippedInput);
+
+      generalError = "toInt: Could not convert ${escapeNixString str} to int.";
+
+      octalAmbigError = "toInt: Ambiguity in interpretation of ${escapeNixString str}"
+      + " between octal and zero padded integer.";
+
+    in
+      # Error on presence of non digit characters.
+      if strippedInput == null
+      then throw generalError
+      # Error on presence of leading zero/octal ambiguity.
+      else if isLeadingZero
+      then throw octalAmbigError
+      # Error if parse function fails.
+      else if !isInt parsedInput
+      then throw generalError
+      # Return result.
+      else parsedInput;
+
+
+  /* Parse a string as a base 10 int. This supports parsing of zero-padded integers.
+
+     Type: string -> int
+
+     Example:
+       toIntBase10 "1337"
+       => 1337
+
+       toIntBase10 "-4"
+       => -4
+
+       toIntBase10 " 123 "
+       => 123
+
+       toIntBase10 "00024"
+       => 24
+
+       toIntBase10 "3.14"
+       => error: floating point JSON numbers are not supported
+  */
+  toIntBase10 = str:
+    let
+      # RegEx: Match any leading whitespace, then match any zero padding, capture any remaining
+      # digits after that, and finally match any trailing whitespace.
+      strippedInput = match "[[:space:]]*0*([[:digit:]]+)[[:space:]]*" str;
+
+      # RegEx: Match at least one '0'.
+      isZero = match "0+" (head strippedInput) == [];
+
+      # Attempt to parse input
+      parsedInput = fromJSON (head strippedInput);
+
+      generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";
+
+    in
+      # Error on presence of non digit characters.
+      if strippedInput == null
+      then throw generalError
+      # In the special case zero-padded zero (00000), return early.
+      else if isZero
+      then 0
+      # Error if parse function fails.
+      else if !isInt parsedInput
+      then throw generalError
+      # Return result.
+      else parsedInput;
 
   /* Read a list of paths from `file`, relative to the `rootPath`.
      Lines beginning with `#` are treated as comments and ignored.