diff --git a/fish-rust/src/wchar.rs b/fish-rust/src/wchar.rs index a01db1782..5e6be70b1 100644 --- a/fish-rust/src/wchar.rs +++ b/fish-rust/src/wchar.rs @@ -6,6 +6,9 @@ pub use widestring::{Utf32Str as wstr, Utf32String as WString}; +/// Pull in our extensions. +pub use crate::wchar_ext::{IntoCharIter, WExt}; + /// Creates a wstr string slice, like the "L" prefix of C++. /// The result is of type wstr. /// It is NOT nul-terminated. @@ -27,9 +30,6 @@ macro_rules! L { /// Note: the resulting string is NOT nul-terminated. pub use widestring_suffix::widestrs; -/// Pull in our extensions. -pub use crate::wchar_ext::{CharPrefixSuffix, WExt}; - // Use Unicode "non-characters" for internal characters as much as we can. This // gives us 32 "characters" for internal use that we can guarantee should not // appear in our input stream. See http://www.unicode.org/faq/private_use.html. diff --git a/fish-rust/src/wchar_ext.rs b/fish-rust/src/wchar_ext.rs index 720e987f4..47253d009 100644 --- a/fish-rust/src/wchar_ext.rs +++ b/fish-rust/src/wchar_ext.rs @@ -80,35 +80,36 @@ fn test_to_wstring() { assert_eq!(i64::MAX.to_wstring(), "9223372036854775807"); } -/// A thing that a wide string can start with or end with. -/// It must have a chars() method which returns a double-ended char iterator. -pub trait CharPrefixSuffix { - type Iter: DoubleEndedIterator; +/// A trait for a thing that can produce a double-ended, cloneable +/// iterator of chars. +/// Common implementations include char, &str, &wstr, &WString. +pub trait IntoCharIter { + type Iter: DoubleEndedIterator + Clone; fn chars(self) -> Self::Iter; } -impl CharPrefixSuffix for char { +impl IntoCharIter for char { type Iter = std::iter::Once; fn chars(self) -> Self::Iter { std::iter::once(self) } } -impl<'a> CharPrefixSuffix for &'a str { +impl<'a> IntoCharIter for &'a str { type Iter = std::str::Chars<'a>; fn chars(self) -> Self::Iter { str::chars(self) } } -impl<'a> CharPrefixSuffix for &'a wstr { +impl<'a> IntoCharIter for &'a wstr { type Iter = CharsUtf32<'a>; fn chars(self) -> Self::Iter { wstr::chars(self) } } -impl<'a> CharPrefixSuffix for &'a WString { +impl<'a> IntoCharIter for &'a WString { type Iter = CharsUtf32<'a>; fn chars(self) -> Self::Iter { wstr::chars(self) @@ -155,13 +156,13 @@ fn find_char(&self, c: char) -> Option { /// \return whether we start with a given Prefix. /// The Prefix can be a char, a &str, a &wstr, or a &WString. - fn starts_with(&self, prefix: Prefix) -> bool { + fn starts_with(&self, prefix: Prefix) -> bool { iter_prefixes_iter(prefix.chars(), self.as_char_slice().iter().copied()) } /// \return whether we end with a given Suffix. /// The Suffix can be a char, a &str, a &wstr, or a &WString. - fn ends_with(&self, suffix: Suffix) -> bool { + fn ends_with(&self, suffix: Suffix) -> bool { iter_prefixes_iter( suffix.chars().rev(), self.as_char_slice().iter().copied().rev(), diff --git a/fish-rust/src/wutil/errors.rs b/fish-rust/src/wutil/errors.rs new file mode 100644 index 000000000..243ee082f --- /dev/null +++ b/fish-rust/src/wutil/errors.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Error { + // The value overflowed. + Overflow, + + // The input string was empty. + Empty, + + // The input string contained an invalid char. + // Note this may not be returned for conversions which stop at invalid chars. + InvalidChar, + + // There were chars remaining in the input. + CharsLeft, +} diff --git a/fish-rust/src/wutil/mod.rs b/fish-rust/src/wutil/mod.rs index 39a5be1b6..56e11c70f 100644 --- a/fish-rust/src/wutil/mod.rs +++ b/fish-rust/src/wutil/mod.rs @@ -1,7 +1,9 @@ +pub mod errors; pub mod format; pub mod gettext; mod normalize_path; -mod wcstoi; +pub mod wcstod; +pub mod wcstoi; mod wrealpath; use std::io::Write; diff --git a/fish-rust/src/wutil/wcstod.rs b/fish-rust/src/wutil/wcstod.rs new file mode 100644 index 000000000..82268453c --- /dev/null +++ b/fish-rust/src/wutil/wcstod.rs @@ -0,0 +1,444 @@ +use super::errors::Error; +use crate::wchar::IntoCharIter; +use fast_float::parse_partial_iter; + +/// Parses a 64-bit floating point number. +/// +/// Leading whitespace and trailing characters are ignored. If the input +/// string does not contain a valid floating point number (where e.g. +/// `"."` is seen as a valid floating point number), `None` is returned. +/// Otherwise the parsed floating point number is returned. +/// +/// The `decimal_sep` parameter is used to specify the decimal separator. +/// '.' is a normal default. +/// +/// The `consumed` parameter is used to return the number of characters +/// consumed, similar to the "end" parameter to strtod. +/// This is only meaningful if parsing succeeds. +/// +/// Error::Overflow is returned if the value is too large in magnitude. +pub fn wcstod(input: Chars, decimal_sep: char, consumed: &mut usize) -> Result +where + Chars: IntoCharIter, +{ + let chars = input.chars(); + if chars.clone().next().is_none() { + *consumed = 0; + return Err(Error::Empty); + } + + let ret = parse_partial_iter(chars.clone().fuse(), decimal_sep); + if ret.is_err() { + *consumed = 0; + return Err(Error::InvalidChar); + } + let (val, n): (f64, usize) = ret.unwrap(); + + // Fast-float does not return overflow errors; instead it just returns +/- infinity. + // Check to see if the first character is a digit or the decimal; if so that indicates overflow. + if val.is_infinite() { + for c in chars { + if c.is_whitespace() { + continue; + } else if c.is_ascii_digit() || c == decimal_sep { + return Err(Error::Overflow); + } else { + break; + } + } + } + *consumed = n; + Ok(val) +} + +#[cfg(test)] +mod test { + #![allow(overflowing_literals)] + + use super::{wcstod, Error}; + use std::f64; + + #[test] + #[allow(clippy::all)] + pub fn tests() { + test("12.345", Ok(12.345)); + test("12.345e19", Ok(12.345e19)); + test("-.1e+9", Ok(-0.1e+9)); + test(".125", Ok(0.125)); + test("1e20", Ok(1e20)); + test("0e-19", Ok(0.0)); + test_consumed("4\00012", Ok(4.0), 1); + test("5.9e-76", Ok(5.9e-76)); + test("", Err(Error::Empty)); + test("Inf", Ok(f64::INFINITY)); + test("-Inf", Ok(f64::NEG_INFINITY)); + test("+InFiNiTy", Ok(f64::INFINITY)); + test("1e-324", Ok(0.0)); + test( + "+1.000000000116415321826934814453125", + Ok(1.000000000116415321826934814453125), + ); + test("42.0000000000000000001", Ok(42.0000000000000000001)); + test("42.00000000000000000001", Ok(42.00000000000000000001)); + test("42.000000000000000000001", Ok(42.000000000000000000001)); + test("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368", Ok(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000)); + test_consumed("1y", Ok(1.0), 1); + test_consumed("0.y", Ok(0.0), 2); + test_consumed(".0y", Ok(0.0), 2); + test_consumed("000,,,e1", Ok(0.0), 3); + test("000e1", Ok(0.0)); + test_consumed("000,1e1", Ok(0.0), 3); + test("0", Ok(0.0)); + test("000", Ok(0.0)); + test("-0", Ok(-0.0)); + test("-000", Ok(-0.0)); + test_consumed("0,", Ok(0.0), 1); + test_consumed("-0,", Ok(-0.0), 2); + test_consumed("0,0", Ok(0.0), 1); + test_consumed("-0,0", Ok(-0.0), 2); + test("0e-10", Ok(0.0)); + test("-0e-10", Ok(-0.0)); + test_consumed("0,e-10", Ok(0.0), 1); + test_consumed("-0,e-10", Ok(-0.0), 2); + test_consumed("0,0e-10", Ok(0.0), 1); + test_consumed("-0,0e-10", Ok(-0.0), 2); + test("0e-1000000", Ok(0.0)); + test("-0e-1000000", Ok(-0.0)); + test_consumed("0,0e-1000000", Ok(0.0), 1); + test_consumed("-0,0e-1000000", Ok(-0.0), 2); + test("0", Ok(0.0)); + test("000", Ok(0.0)); + test("-0", Ok(-0.0)); + test("-000", Ok(-0.0)); + test("0e-10", Ok(0.0)); + test("-0e-10", Ok(-0.0)); + test("0e-1000000", Ok(0.0)); + test("-0e-1000000", Ok(-0.0)); + test("1", Ok(1_f64)); + test("1.1", Ok(1.1)); + test("1.1e1", Ok(1.1e1)); + test("1234.1234", Ok(1234.1234)); + test("1234.12345678", Ok(1234.12345678)); + test("1234.123456789012", Ok(1234.123456789012)); + test( + "1.797693134862315708145274237317e+10", + Ok(1.797693134862315708145274237317e+10), + ); + test( + "1.797693134862315708145274237317e+308", + Ok(1.797693134862315708145274237317e+308_f64), + ); + test("000000000e123", Ok(0.0)); + test("0000000010000e-329", Ok(0.0)); + test("000000001e-325", Ok(0.0)); + test("0000000020000e-328", Ok(0.0)); + test("0000000090000e-329", Ok(0.0)); + test("0e+999", Ok(0.0)); + test("0e1", Ok(0.0)); + test("0e12345", Ok(0.0)); + test("0e2", Ok(0.0)); + test("0e-2", Ok(0.0)); + test("0e-999", Ok(0.0)); + test("10000e-329", Ok(0.0)); + test("1e-325", Ok(0.0)); + test("20000e-328", Ok(0.0)); + test("2e-324", Ok(0.0)); + test("90000e-329", Ok(0.0)); + test_consumed("e1324", Err(Error::InvalidChar), 0); + test("1e0", Ok(1.0)); + test("17976931348623157e292", Ok(1.7976931348623157E+308)); + test("17976931348623158e292", Ok(1.7976931348623158E+308)); + test("1e1", Ok(10.0)); + test("1e2", Ok(100.0)); + test( + "10141204801825834086073718800384e0", + Ok(10141204801825834086073718800384.0), + ); + test( + "1014120480182583464902367222169599999e-5", + Ok(10141204801825834086073718800384.0), + ); + test( + "1014120480182583464902367222169600001e-5", + Ok(10141204801825835211973625643008.0), + ); + test( + "10141204801825834649023672221696e0", + Ok(10141204801825835211973625643008.0), + ); + test( + "10141204801825835211973625643008e0", + Ok(10141204801825835211973625643008.0), + ); + test("104110013277974872254e-225", Ok(104110013277974872254e-225)); + test("12345e0", Ok(12345.0)); + test("12345e1", Ok(123450.0)); + test("12345e2", Ok(1234500.0)); + test("12345678901234e0", Ok(12345678901234.0)); + test("12345678901234e1", Ok(123456789012340.0)); + test("12345678901234e2", Ok(1234567890123400.0)); + test("123456789012345e0", Ok(123456789012345.0)); + test("123456789012345e1", Ok(1234567890123450.0)); + test("123456789012345e2", Ok(12345678901234500.0)); + test( + "1234567890123456789012345e108", + Ok(1234567890123456789012345e108), + ); + test( + "1234567890123456789012345e109", + Ok(1234567890123456789012345e109), + ); + test( + "1234567890123456789012345e110", + Ok(1234567890123456789012345e110), + ); + test( + "1234567890123456789012345e111", + Ok(1234567890123456789012345e111), + ); + test( + "1234567890123456789012345e112", + Ok(1234567890123456789012345e112), + ); + test( + "1234567890123456789012345e113", + Ok(1234567890123456789012345e113), + ); + test( + "1234567890123456789012345e114", + Ok(1234567890123456789012345e114), + ); + test( + "1234567890123456789012345e115", + Ok(1234567890123456789012345e115), + ); + test( + "1234567890123456789052345e108", + Ok(1234567890123456789052345e108), + ); + test( + "1234567890123456789052345e109", + Ok(1234567890123456789052345e109), + ); + test( + "1234567890123456789052345e110", + Ok(1234567890123456789052345e110), + ); + test( + "1234567890123456789052345e111", + Ok(1234567890123456789052345e111), + ); + test( + "1234567890123456789052345e112", + Ok(1234567890123456789052345e112), + ); + test( + "1234567890123456789052345e113", + Ok(1234567890123456789052345e113), + ); + test( + "1234567890123456789052345e114", + Ok(1234567890123456789052345e114), + ); + test( + "1234567890123456789052345e115", + Ok(1234567890123456789052345e115), + ); + test("123456789012345e-1", Ok(123456789012345e-1)); + test("123456789012345e-2", Ok(123456789012345e-2)); + test("123456789012345e20", Ok(123456789012345e20)); + test("123456789012345e-20", Ok(123456789012345e-20)); + test("123456789012345e22", Ok(123456789012345e22)); + test("123456789012345e-22", Ok(123456789012345e-22)); + test("123456789012345e23", Ok(123456789012345e23)); + test("123456789012345e-23", Ok(123456789012345e-23)); + test("123456789012345e-25", Ok(123456789012345e-25)); + test("123456789012345e35", Ok(123456789012345e35)); + test("123456789012345e36", Ok(123456789012345e36)); + test("123456789012345e37", Ok(123456789012345e37)); + test("123456789012345e39", Ok(123456789012345e39)); + test("123456789012345e-39", Ok(123456789012345e-39)); + test("123456789012345e-5", Ok(123456789012345e-5)); + test("12345678901234e-1", Ok(12345678901234e-1)); + test("12345678901234e-2", Ok(12345678901234e-2)); + test("12345678901234e20", Ok(12345678901234e20)); + test("12345678901234e-20", Ok(12345678901234e-20)); + test("12345678901234e22", Ok(12345678901234e22)); + test("12345678901234e-22", Ok(12345678901234e-22)); + test("12345678901234e23", Ok(12345678901234e23)); + test("12345678901234e-23", Ok(12345678901234e-23)); + test("12345678901234e-25", Ok(12345678901234e-25)); + test("12345678901234e30", Ok(12345678901234e30)); + test("12345678901234e31", Ok(12345678901234e31)); + test("12345678901234e32", Ok(12345678901234e32)); + test("12345678901234e35", Ok(12345678901234e35)); + test("12345678901234e36", Ok(12345678901234e36)); + test("12345678901234e37", Ok(12345678901234e37)); + test("12345678901234e-39", Ok(12345678901234e-39)); + test("12345678901234e-5", Ok(12345678901234e-5)); + test("123456789e108", Ok(123456789e108)); + test("123456789e109", Ok(123456789e109)); + test("123456789e110", Ok(123456789e110)); + test("123456789e111", Ok(123456789e111)); + test("123456789e112", Ok(123456789e112)); + test("123456789e113", Ok(123456789e113)); + test("123456789e114", Ok(123456789e114)); + test("123456789e115", Ok(123456789e115)); + test("12345e-1", Ok(12345e-1)); + test("12345e-2", Ok(12345e-2)); + test("12345e20", Ok(12345e20)); + test("12345e-20", Ok(12345e-20)); + test("12345e22", Ok(12345e22)); + test("12345e-22", Ok(12345e-22)); + test("12345e23", Ok(12345e23)); + test("12345e-23", Ok(12345e-23)); + test("12345e-25", Ok(12345e-25)); + test("12345e30", Ok(12345e30)); + test("12345e31", Ok(12345e31)); + test("12345e32", Ok(12345e32)); + test("12345e35", Ok(12345e35)); + test("12345e36", Ok(12345e36)); + test("12345e37", Ok(12345e37)); + test("12345e-39", Ok(12345e-39)); + test("12345e-5", Ok(12345e-5)); + test("000000001234e304", Ok(1234e304)); + test("0000000123400000e299", Ok(1234e304)); + test("123400000e299", Ok(1234e304)); + test("1234e304", Ok(1234e304)); + test("00000000123400000e300", Ok(1234e305)); + test("00000001234e305", Ok(1234e305)); + test("123400000e300", Ok(1234e305)); + test("1234e305", Ok(1234e305)); + test("00000000170000000e300", Ok(17e307)); + test("0000000017e307", Ok(17e307)); + test("170000000e300", Ok(17e307)); + test("17e307", Ok(17e307)); + test("1e-1", Ok(1e-1)); + test("1e-2", Ok(1e-2)); + test("1e20", Ok(1e20)); + test("1e-20", Ok(1e-20)); + test("1e22", Ok(1e22)); + test("1e-22", Ok(1e-22)); + test("1e23", Ok(1e23)); + test("1e-23", Ok(1e-23)); + test("1e-25", Ok(1e-25)); + test("000000000000100000e303", Ok(1e308)); + test("00000001e308", Ok(1e308)); + test("100000e303", Ok(1e308)); + test("1e308", Ok(1e308)); + test("1e35", Ok(1e35)); + test("1e36", Ok(1e36)); + test("1e37", Ok(1e37)); + test("1e-39", Ok(1e-39)); + test("1e-5", Ok(1e-5)); + test("2e0", Ok(2.0)); + test("22250738585072011e-324", Ok(2.225073858507201e-308)); + test("2e1", Ok(20.0)); + test("2e2", Ok(200.0)); + test("2e-1", Ok(2e-1)); + test("2e-2", Ok(2e-2)); + test("2e20", Ok(2e20)); + test("2e-20", Ok(2e-20)); + test("2e22", Ok(2e22)); + test("2e-22", Ok(2e-22)); + test("2e23", Ok(2e23)); + test("2e-23", Ok(2e-23)); + test("2e-25", Ok(2e-25)); + test("2e35", Ok(2e35)); + test("2e36", Ok(2e36)); + test("2e37", Ok(2e37)); + test("2e-39", Ok(2e-39)); + test("2e-5", Ok(2e-5)); + test("358416272e-33", Ok(358416272e-33)); + test("00000030000e-328", Ok(40000e-328)); + test("30000e-328", Ok(40000e-328)); + test("3e-324", Ok(4e-324)); + test("5445618932859895362967233318697132813618813095743952975439298223406969961560047552942717636670910728746893019786283454139917900193169748259349067524939840552682198095012176093045431437495773903922425632551857520884625114624126588173520906670968542074438852601438992904761759703022688483745081090292688986958251711580854575674815074162979705098246243690189880319928315307816832576838178256307401454285988871020923752587330172447966674453785790265533466496640456213871241930958703059911787722565044368663670643970181259143319016472430928902201239474588139233890135329130660705762320235358869874608541509790266400643191187286648422874774910682648288516244021893172769161449825765517353755844373640588822904791244190695299838293263075467057383813882521706545084301049855505888186560731e-1035", Ok(5.445618932859895e-255)); + test( + "5708990770823838890407843763683279797179383808e0", + Ok(5708990770823838890407843763683279797179383808.0), + ); + test( + "5708990770823839207320493820740630171355185151999e-3", + Ok(5708990770823838890407843763683279797179383808.0), + ); + test( + "5708990770823839207320493820740630171355185152001e-3", + Ok(5708990770823839524233143877797980545530986496.0), + ); + test( + "5708990770823839207320493820740630171355185152e0", + Ok(5708990770823839524233143877797980545530986496.0), + ); + test( + "5708990770823839524233143877797980545530986496e0", + Ok(5708990770823839524233143877797980545530986496.0), + ); + test("72057594037927928e0", Ok(72057594037927928.0)); + test("7205759403792793199999e-5", Ok(72057594037927928.0)); + test("7205759403792793200001e-5", Ok(72057594037927936.0)); + test("72057594037927932e0", Ok(72057594037927936.0)); + test("72057594037927936e0", Ok(72057594037927936.0)); + test("89255e-22", Ok(89255e-22)); + test("9e0", Ok(9.0)); + test("9e1", Ok(90.0)); + test("9e2", Ok(900.0)); + test("9223372036854774784e0", Ok(9223372036854774784.0)); + test("922337203685477529599999e-5", Ok(9223372036854774784.0)); + test("922337203685477529600001e-5", Ok(9223372036854775808.0)); + test("9223372036854775296e0", Ok(9223372036854775808.0)); + test("9223372036854775808e0", Ok(9223372036854775808.0)); + test("9e-1", Ok(9e-1)); + test("9e-2", Ok(9e-2)); + test("9e20", Ok(9e20)); + test("9e-20", Ok(9e-20)); + test("9e22", Ok(9e22)); + test("9e-22", Ok(9e-22)); + test("9e23", Ok(9e23)); + test("9e-23", Ok(9e-23)); + test("9e-25", Ok(9e-25)); + test("9e35", Ok(9e35)); + test("9e36", Ok(9e36)); + test("9e37", Ok(9e37)); + test("9e-39", Ok(9e-39)); + test("9e-5", Ok(9e-5)); + test("00000000180000000e300", Err(Error::Overflow)); + test("0000000018e307", Err(Error::Overflow)); + test("00000001000000e303", Err(Error::Overflow)); + test("0000001e309", Err(Error::Overflow)); + test("1000000e303", Err(Error::Overflow)); + test("17976931348623159e292", Err(Error::Overflow)); + test("180000000e300", Err(Error::Overflow)); + test("18e307", Err(Error::Overflow)); + test("1e309", Err(Error::Overflow)); + test("1e-409", Ok(0.0)); + + test_sep("1,1e1", Ok(1.1e1), ','); + + test_consumed("12345e37randomstuff", Ok(12345e37), 8); + } + + fn test(input: &str, val: Result) { + test_sep(input, val, '.') + } + + fn test_sep(input: &str, val: Result, decimalsep: char) { + let mut consumed = 0; + let result = wcstod(input, decimalsep, &mut consumed); + assert_eq!(result, val); + if result.is_ok() { + assert_eq!(consumed, input.chars().count()); + assert_eq!( + result.unwrap().is_sign_positive(), + val.unwrap().is_sign_positive() + ); + } + } + + fn test_consumed(input: &str, val: Result, exp_consumed: usize) { + let mut consumed = 0; + let result = wcstod(input, '.', &mut consumed); + assert_eq!(result, val); + assert_eq!(consumed, exp_consumed); + } +} diff --git a/fish-rust/src/wutil/wcstoi.rs b/fish-rust/src/wutil/wcstoi.rs index e7d5ae2e3..1164b1b2f 100644 --- a/fish-rust/src/wutil/wcstoi.rs +++ b/fish-rust/src/wutil/wcstoi.rs @@ -1,13 +1,7 @@ +pub use super::errors::Error; use num_traits::{NumCast, PrimInt}; use std::iter::Peekable; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Error { - Overflow, - Empty, - InvalidDigit, - CharsLeft, -} +use std::result::Result; struct ParseResult { result: u64, @@ -98,7 +92,7 @@ fn fish_parse_radix(ichars: Chars, mradix: Option) -> Result( } = fish_parse_radix(src, mradix)?; if !signed && negative { - Err(Error::InvalidDigit) + Err(Error::InvalidChar) } else if consume_all && !consumed_all { Err(Error::CharsLeft) } else if !signed || !negative { @@ -211,8 +205,8 @@ fn tests() { assert_eq!(run1("0"), Ok(0)); assert_eq!(run1("-0"), Ok(0)); assert_eq!(run1("+0"), Ok(0)); - assert_eq!(run1("+-0"), Err(Error::InvalidDigit)); - assert_eq!(run1("-+0"), Err(Error::InvalidDigit)); + assert_eq!(run1("+-0"), Err(Error::InvalidChar)); + assert_eq!(run1("-+0"), Err(Error::InvalidChar)); assert_eq!(run1("123"), Ok(123)); assert_eq!(run1("+123"), Ok(123)); assert_eq!(run1("-123"), Ok(-123)); @@ -225,7 +219,7 @@ fn tests() { assert_eq!(run1("-0123"), Ok(-83)); assert_eq!(run1(" 345 "), Ok(345)); assert_eq!(run1(" -345 "), Ok(-345)); - assert_eq!(run1(" x345"), Err(Error::InvalidDigit)); + assert_eq!(run1(" x345"), Err(Error::InvalidChar)); assert_eq!(run1("456x"), Ok(456)); assert_eq!(run1("456 x"), Ok(456)); assert_eq!(run1("99999999999999999999999"), Err(Error::Overflow));