Unverified Commit ed711738 authored by Jacob Abel's avatar Jacob Abel
Browse files

lib/strings: Update docs and restructured code to improve readability of toInt and toIntBase10.

parent 88b18dcf
Loading
Loading
Loading
Loading
+37 −21
Original line number Diff line number Diff line
@@ -784,7 +784,7 @@ rec {
      false;

  /* Parse a string as an int. Does not support parsing of integers with preceding zero due to
  ambiguity between zero-padded and octal numbers.
  ambiguity between zero-padded and octal numbers. See toIntBase10.

     Type: string -> int

@@ -805,25 +805,35 @@ rec {
       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
      # RegEx: Match any leading whitespace, then any digits, and finally match any trailing
      # whitespace.
      strippedInput = match "[[:space:]]*([[:digit:]]+)[[:space:]]*" str;

      # RegEx: Match any leading whitespace, then a leading '0', then at least one digit following
      # after, and finally match any trailing whitespace.
      isLeadingZero = match "[[:space:]]*0[[: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 (elemAt strippedInput 0);
      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
      if isLeadingZero
      then throw "Ambiguity in interpretation of ${str} between octal and zero padded integer."
      else if strippedInput != null && isInt parsedInput
      then parsedInput
      else throw "Could not convert ${str} to int.";
      # 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.
@@ -846,26 +856,32 @@ rec {
       toIntBase10 "3.14"
       => error: floating point JSON numbers are not supported
  */
  # Obviously, it is a bit hacky to use fromJSON this way.
  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 any leading whitespace, at least one '0', and any trailing whitespace.
      isZero = match "[[:space:]]*0+[[:space:]]*" str == [];
      # RegEx: Match at least one '0'.
      isZero = match "0+" (head strippedInput) == [];

      # Attempt to parse input
      parsedInput = fromJSON (elemAt strippedInput 0);
      parsedInput = fromJSON (head strippedInput);

      generalError = "toIntBase10: Could not convert ${escapeNixString str} to int.";

    in
      # Value is zero
      if isZero
      # 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
      else
      if strippedInput != null && isInt parsedInput
      then parsedInput
      else throw "Could not convert ${str} to int.";
      # 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.
+4 −0
Original line number Diff line number Diff line
@@ -346,6 +346,8 @@ runTests {
    ( 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 " 1d ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt " d0 ") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "00") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "01") == { success = false; value = false; } )
    ( builtins.tryEval (toInt "002") == { success = false; value = false; } )
@@ -388,6 +390,8 @@ runTests {
    ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " 1d ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " d0 ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } )
    ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } )
+1 −1
Original line number Diff line number Diff line
@@ -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 'Could not convert .* to int' config.value ./declare-coerced-value-unsound.nix ./define-value-string-arbitrary.nix
checkConfigError 'toInt: 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