simplify from_str and start adding support for 2, 8, 16 radixes

This commit is contained in:
Trevor Spiteri 2019-08-10 15:34:49 +02:00
parent e6f64a44b2
commit 852040e876
1 changed files with 140 additions and 207 deletions

View File

@ -28,7 +28,7 @@ use core::{
// 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16 // 5^3 × 2 < 2^8 => (10^3 - 1) × 2^(8-3+1) < 2^16
// Returns None for large fractions that are rounded to 1.0 // Returns None for large fractions that are rounded to 1.0
pub fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option<u8> { fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option<u8> {
debug_assert!(a < 10u16.pow(3)); debug_assert!(a < 10u16.pow(3));
debug_assert!(dump_bits <= 8); debug_assert!(dump_bits <= 8);
let divisor = 5u16.pow(3) * 2; let divisor = 5u16.pow(3) * 2;
@ -42,7 +42,7 @@ pub fn dec3_to_bin8(a: u16, dump_bits: u32) -> Option<u8> {
} }
// 5^6 × 2 < 2^16 => (10^6 - 1) × 2^(16-6+1) < 2^32 // 5^6 × 2 < 2^16 => (10^6 - 1) × 2^(16-6+1) < 2^32
// Returns None for large fractions that are rounded to 1.0 // Returns None for large fractions that are rounded to 1.0
pub fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option<u16> { fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option<u16> {
debug_assert!(a < 10u32.pow(6)); debug_assert!(a < 10u32.pow(6));
debug_assert!(dump_bits <= 16); debug_assert!(dump_bits <= 16);
let divisor = 5u32.pow(6) * 2; let divisor = 5u32.pow(6) * 2;
@ -56,7 +56,7 @@ pub fn dec6_to_bin16(a: u32, dump_bits: u32) -> Option<u16> {
} }
// 5^13 × 2 < 2^32 => (10^13 - 1) × 2^(32-13+1) < 2^64 // 5^13 × 2 < 2^32 => (10^13 - 1) × 2^(32-13+1) < 2^64
// Returns None for large fractions that are rounded to 1.0 // Returns None for large fractions that are rounded to 1.0
pub fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option<u32> { fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option<u32> {
debug_assert!(a < 10u64.pow(13)); debug_assert!(a < 10u64.pow(13));
debug_assert!(dump_bits <= 32); debug_assert!(dump_bits <= 32);
let divisor = 5u64.pow(13) * 2; let divisor = 5u64.pow(13) * 2;
@ -70,7 +70,7 @@ pub fn dec13_to_bin32(a: u64, dump_bits: u32) -> Option<u32> {
} }
// 5^27 × 2 < 2^64 => (10^27 - 1) × 2^(64-27+1) < 2^128 // 5^27 × 2 < 2^64 => (10^27 - 1) × 2^(64-27+1) < 2^128
// Returns None for large fractions that are rounded to 1.0 // Returns None for large fractions that are rounded to 1.0
pub fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option<u64> { fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option<u64> {
debug_assert!(a < 10u128.pow(27)); debug_assert!(a < 10u128.pow(27));
debug_assert!(dump_bits <= 64); debug_assert!(dump_bits <= 64);
let divisor = 5u128.pow(27) * 2; let divisor = 5u128.pow(27) * 2;
@ -84,7 +84,7 @@ pub fn dec27_to_bin64(a: u128, dump_bits: u32) -> Option<u64> {
} }
// 5^54 × 2 < 2^128 => (10^54 - 1) × 2^(128-54+1) < 2^256 // 5^54 × 2 < 2^128 => (10^54 - 1) × 2^(128-54+1) < 2^256
// Returns None for large fractions that are rounded to 1.0 // Returns None for large fractions that are rounded to 1.0
pub fn dec27_27_to_bin128(hi: u128, lo: u128, dump_bits: u32) -> Option<u128> { fn dec27_27_to_bin128(hi: u128, lo: u128, dump_bits: u32) -> Option<u128> {
debug_assert!(hi < 10u128.pow(27)); debug_assert!(hi < 10u128.pow(27));
debug_assert!(lo < 10u128.pow(27)); debug_assert!(lo < 10u128.pow(27));
debug_assert!(dump_bits <= 128); debug_assert!(dump_bits <= 128);
@ -210,7 +210,7 @@ impl Display for ParseFixedError {
} }
} }
fn parse(s: &str, can_be_neg: bool) -> Result<Parse<'_>, ParseFixedError> { fn parse(s: &str, can_be_neg: bool, radix: i32) -> Result<Parse<'_>, ParseFixedError> {
let mut int = (0, 0); let mut int = (0, 0);
let mut frac = (0, 0); let mut frac = (0, 0);
let mut has_sign = false; let mut has_sign = false;
@ -218,20 +218,20 @@ fn parse(s: &str, can_be_neg: bool) -> Result<Parse<'_>, ParseFixedError> {
let mut has_digits = false; let mut has_digits = false;
let mut has_point = false; let mut has_point = false;
for (index, c) in s.char_indices() { for (index, c) in s.char_indices() {
if c == '.' { match (radix, c) {
err!(has_point, TooManyPoints); (_, '.') => {
has_digits = false; err!(has_point, TooManyPoints);
has_point = true; has_digits = false;
frac.0 = index + c.len_utf8(); has_point = true;
continue; frac.0 = index + c.len_utf8();
} continue;
match c { }
'+' => { (_, '+') => {
err!(has_point || has_sign || has_digits, InvalidDigit); err!(has_point || has_sign || has_digits, InvalidDigit);
has_sign = true; has_sign = true;
continue; continue;
} }
'-' => { (_, '-') => {
err!( err!(
has_point || has_sign || has_digits || !can_be_neg, has_point || has_sign || has_digits || !can_be_neg,
InvalidDigit InvalidDigit
@ -240,7 +240,12 @@ fn parse(s: &str, can_be_neg: bool) -> Result<Parse<'_>, ParseFixedError> {
is_negative = true; is_negative = true;
continue; continue;
} }
'0'..='9' => { (2, '0'..='1')
| (8, '0'..='7')
| (10, '0'..='9')
| (16, '0'..='9')
| (16, 'a'..='f')
| (16, 'A'..='F') => {
if !has_point && !has_digits { if !has_point && !has_digits {
int.0 = index; int.0 = index;
} }
@ -276,7 +281,7 @@ macro_rules! impl_from_str {
type Err = ParseFixedError; type Err = ParseFixedError;
#[inline] #[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
$method(s, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits) $method(s, 10, Self::int_nbits(), Self::frac_nbits()).map(Self::from_bits)
} }
} }
}; };
@ -284,16 +289,21 @@ macro_rules! impl_from_str {
macro_rules! impl_from_str_signed { macro_rules! impl_from_str_signed {
( (
($Fixed:ident, $NBits:ident, $Bits:ident, $HalfBits:ident, $DoubleBits:ident, $one:expr); $Fixed:ident, $NBits:ident, $Bits:ident;
fn $all:ident; fn $all:ident;
fn $int:ident, $int_half:expr; fn $int:ident, ($int_half:ident, $int_half_cond:expr);
$frac:ident; $frac:ident;
) => { ) => {
impl_from_str! { $Fixed, $NBits, $all } impl_from_str! { $Fixed, $NBits, $all }
fn $all(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<$Bits, ParseFixedError> { fn $all(
let Parse { neg, int, frac } = parse(s, true)?; s: &str,
let (frac, whole_frac) = match $frac(frac, frac_nbits) { radix: i32,
int_nbits: u32,
frac_nbits: u32,
) -> Result<$Bits, ParseFixedError> {
let Parse { neg, int, frac } = parse(s, true, 10)?;
let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) {
Some(frac) => (frac, false), Some(frac) => (frac, false),
None => (0, true), None => (0, true),
}; };
@ -313,22 +323,19 @@ macro_rules! impl_from_str_signed {
} else { } else {
frac as $Bits frac as $Bits
}; };
let int = $int(neg, int, int_nbits, whole_frac)?; let int = $int(neg, int, radix, int_nbits, whole_frac)?;
Ok(int | frac) Ok(int | frac)
} }
fn $int( fn $int(
neg: bool, neg: bool,
int: &str, int: &str,
radix: i32,
nbits: u32, nbits: u32,
whole_frac: bool, whole_frac: bool,
) -> Result<$Bits, ParseFixedError> { ) -> Result<$Bits, ParseFixedError> {
let half: Option<fn(bool, &str, u32, bool) -> Result<$HalfBits, ParseFixedError>> = if $int_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 {
$int_half; return $int_half(neg, int, radix, nbits, whole_frac).map($Bits::from);
if let Some(half) = half {
if nbits <= <$Bits as SealedInt>::NBITS / 2 {
return half(neg, int, nbits, whole_frac).map($Bits::from);
}
} }
let mut int = int; let mut int = int;
while int.starts_with('0') { while int.starts_with('0') {
@ -339,11 +346,11 @@ macro_rules! impl_from_str_signed {
return Ok(0); return Ok(0);
} }
let max_abs_int = if neg { let max_abs_int = if neg {
$one << (nbits - 1) <$Bits as SealedInt>::Unsigned::MSB
} else { } else {
($one << (nbits - 1)) - 1 <$Bits as SealedInt>::Unsigned::MSB - 1
}; };
let mut acc = match int.parse::<$DoubleBits>() { let mut acc = match int.parse::<<$Bits as SealedInt>::Unsigned>() {
Ok(i) => { Ok(i) => {
err!(i > max_abs_int, Overflow); err!(i > max_abs_int, Overflow);
i i
@ -366,30 +373,36 @@ macro_rules! impl_from_str_signed {
macro_rules! impl_from_str_unsigned { macro_rules! impl_from_str_unsigned {
( (
($Fixed:ident, $NBits:ident, $Bits:ident, $HalfBits:ident, $DoubleBits:ident, $one:expr); $Fixed:ident, $NBits:ident, $Bits:ident;
fn $all:ident; fn $all:ident;
fn $int:ident, $int_half:expr; fn $int:ident, ($int_half:ident, $int_half_cond:expr);
fn $frac:ident, $frac_half:expr; $frac:ident;
$decode_frac:ident, $dec_frac_digits:expr;
) => { ) => {
impl_from_str! { $Fixed, $NBits, $all } impl_from_str! { $Fixed, $NBits, $all }
fn $all(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<$Bits, ParseFixedError> { fn $all(
let Parse { int, frac, .. } = parse(s, false)?; s: &str,
let (frac, whole_frac) = match $frac(frac, frac_nbits) { radix: i32,
int_nbits: u32,
frac_nbits: u32,
) -> Result<$Bits, ParseFixedError> {
let Parse { int, frac, .. } = parse(s, false, 10)?;
let (frac, whole_frac) = match $frac(frac, radix, frac_nbits) {
Some(frac) => (frac, false), Some(frac) => (frac, false),
None => (0, true), None => (0, true),
}; };
let int = $int(int, int_nbits, whole_frac)?; let int = $int(int, radix, int_nbits, whole_frac)?;
Ok(int | frac) Ok(int | frac)
} }
fn $int(int: &str, nbits: u32, whole_frac: bool) -> Result<$Bits, ParseFixedError> { fn $int(
let half: Option<fn(&str, u32, bool) -> Result<$HalfBits, ParseFixedError>> = $int_half; int: &str,
if let Some(half) = half { radix: i32,
if nbits <= <$Bits as SealedInt>::NBITS / 2 { nbits: u32,
return half(int, nbits, whole_frac).map($Bits::from); whole_frac: bool,
} ) -> Result<$Bits, ParseFixedError> {
if $int_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 {
return $int_half(int, radix, nbits, whole_frac).map($Bits::from);
} }
let mut int = int; let mut int = int;
while int.starts_with('0') { while int.starts_with('0') {
@ -399,204 +412,124 @@ macro_rules! impl_from_str_unsigned {
err!(whole_frac || !int.is_empty(), Overflow); err!(whole_frac || !int.is_empty(), Overflow);
return Ok(0); return Ok(0);
} }
let max_abs_int = ($one << nbits) - 1; let mut acc = match int.parse::<$Bits>() {
let mut acc = match int.parse::<$DoubleBits>() { Ok(i) => i,
Ok(i) => {
err!(i > max_abs_int, Overflow);
i
}
Err(_) => err!(Overflow), Err(_) => err!(Overflow),
}; };
if whole_frac { if whole_frac {
acc += 1; acc = match acc.overflowing_add(1) {
err!(acc > max_abs_int, Overflow); (acc, false) => acc,
(_, true) => err!(Overflow),
};
} }
Ok((acc as $Bits) << (<$Bits as SealedInt>::NBITS - nbits)) Ok(acc << (<$Bits as SealedInt>::NBITS - nbits))
}
};
}
macro_rules! impl_from_str_unsigned_not128 {
(
$Fixed:ident, $NBits:ident, $Bits:ident;
fn $all:ident;
fn $int:ident, ($int_half:ident, $int_half_cond:expr);
fn $frac:ident, ($frac_half:ident, $frac_half_cond:expr);
$decode_frac:ident, $dec_frac_digits:expr, $DoubleBits:ident;
) => {
impl_from_str_unsigned! {
$Fixed, $NBits, $Bits;
fn $all;
fn $int, ($int_half, $int_half_cond);
$frac;
} }
fn $frac(frac: &str, nbits: u32) -> Option<$Bits> { fn $frac(frac: &str, radix: i32, nbits: u32) -> Option<$Bits> {
let half: Option<fn(&str, u32) -> Option<$HalfBits>> = $frac_half; if $frac_half_cond && nbits <= <$Bits as SealedInt>::NBITS / 2 {
if let Some(half) = half { return $frac_half(frac, radix, nbits).map($Bits::from);
if nbits <= <$Bits as SealedInt>::NBITS / 2 {
return half(frac, nbits).map($Bits::from);
}
} }
if frac.is_empty() { if frac.is_empty() {
return Some(0); return Some(0);
} }
let end = cmp::min(frac.len(), $dec_frac_digits); let end = cmp::min(frac.len(), $dec_frac_digits);
let rem = $dec_frac_digits - end; let rem = $dec_frac_digits - end;
let i = frac[..end].parse::<$DoubleBits>().unwrap() * ($one * 10).pow(rem as u32); let ten: $DoubleBits = 10;
let i = frac[..end].parse::<$DoubleBits>().unwrap() * ten.pow(rem as u32);
$decode_frac(i, <$Bits as SealedInt>::NBITS - nbits) $decode_frac(i, <$Bits as SealedInt>::NBITS - nbits)
} }
}; };
} }
impl_from_str_signed! { impl_from_str_signed! {
(FixedI8, U8, i8, bool, u16, 1u16); FixedI8, U8, i8;
fn from_str_i8; fn from_str_i8;
fn get_int_i8, None; fn get_int_i8, (get_int_i8, false);
get_frac8; get_frac8;
} }
impl_from_str_unsigned! { impl_from_str_unsigned_not128! {
(FixedU8, U8, u8, bool, u16, 1u16); FixedU8, U8, u8;
fn from_str_u8; fn from_str_u8;
fn get_int_u8, None; fn get_int_u8, (get_int_u8, false);
fn get_frac8, None; fn get_frac8, (get_frac8, false);
dec3_to_bin8, 3; dec3_to_bin8, 3, u16;
} }
impl_from_str_signed! { impl_from_str_signed! {
(FixedI16, U16, i16, i8, u32, 1u32); FixedI16, U16, i16;
fn from_str_i16; fn from_str_i16;
fn get_int_i16, Some(get_int_i8); fn get_int_i16, (get_int_i8, true);
get_frac16; get_frac16;
} }
impl_from_str_unsigned! { impl_from_str_unsigned_not128! {
(FixedU16, U16, u16, u8, u32, 1u32); FixedU16, U16, u16;
fn from_str_u16; fn from_str_u16;
fn get_int_u16, Some(get_int_u8); fn get_int_u16, (get_int_u8, true);
fn get_frac16, Some(get_frac8); fn get_frac16, (get_frac8, true);
dec6_to_bin16, 6; dec6_to_bin16, 6, u32;
} }
impl_from_str_signed! { impl_from_str_signed! {
(FixedI32, U32, i32, i16, u64, 1u64); FixedI32, U32, i32;
fn from_str_i32; fn from_str_i32;
fn get_int_i32, Some(get_int_i16); fn get_int_i32, (get_int_i16, true);
get_frac32; get_frac32;
} }
impl_from_str_unsigned! { impl_from_str_unsigned_not128! {
(FixedU32, U32, u32, u16, u64, 1u64); FixedU32, U32, u32;
fn from_str_u32; fn from_str_u32;
fn get_int_u32, Some(get_int_u16); fn get_int_u32, (get_int_u16, true);
fn get_frac32, Some(get_frac16); fn get_frac32, (get_frac16, true);
dec13_to_bin32, 13; dec13_to_bin32, 13, u64;
} }
impl_from_str_signed! { impl_from_str_signed! {
(FixedI64, U64, i64, i32, u128, 1u128); FixedI64, U64, i64;
fn from_str_i64; fn from_str_i64;
fn get_int_i64, Some(get_int_i32); fn get_int_i64, (get_int_i32, true);
get_frac64; get_frac64;
} }
impl_from_str_unsigned! { impl_from_str_unsigned_not128! {
(FixedU64, U64, u64, u32, u128, 1u128); FixedU64, U64, u64;
fn from_str_u64; fn from_str_u64;
fn get_int_u64, Some(get_int_u32); fn get_int_u64, (get_int_u32, true);
fn get_frac64, Some(get_frac32); fn get_frac64, (get_frac32, true);
dec27_to_bin64, 27; dec27_to_bin64, 27, u128;
} }
impl_from_str! { FixedI128, U128, from_str_i128 } impl_from_str_signed! {
FixedI128, U128, i128;
fn from_str_i128(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<i128, ParseFixedError> { fn from_str_i128;
let Parse { neg, int, frac } = parse(s, true)?; fn get_int_i128, (get_int_i64, true);
let (frac, whole_frac) = match get_frac128(frac, frac_nbits) { get_frac128;
Some(frac) => (frac, false), }
None => (0, true), impl_from_str_unsigned! {
}; FixedU128, U128, u128;
let frac = if frac_nbits == <i128>::NBITS { fn from_str_u128;
// special case: no int bits fn get_int_u128, (get_int_u64, true);
if neg { get_frac128;
if frac > <i128 as SealedInt>::Unsigned::MSB {
err!(Overflow)
}
frac.wrapping_neg() as i128
} else {
if frac >= <i128 as SealedInt>::Unsigned::MSB {
err!(Overflow)
}
frac as i128
}
} else {
frac as i128
};
let int = get_int_i128(neg, int, int_nbits, whole_frac)?;
Ok(int | frac)
} }
fn get_int_i128( fn get_frac128(frac: &str, radix: i32, nbits: u32) -> Option<u128> {
neg: bool,
int: &str,
nbits: u32,
whole_frac: bool,
) -> Result<i128, ParseFixedError> {
if nbits <= 64 { if nbits <= 64 {
return get_int_i64(neg, int, nbits, whole_frac).map(i128::from); return get_frac64(frac, radix, nbits).map(u128::from);
}
let mut int = int;
while int.starts_with('0') {
int = &int[1..];
}
if nbits == 0 {
err!(whole_frac || !int.is_empty(), Overflow);
return Ok(0);
}
let max_abs_int = if neg {
1u128 << (nbits - 1)
} else {
(1u128 << (nbits - 1)) - 1
};
let mut acc = match int.parse::<u128>() {
Ok(i) => {
err!(i > max_abs_int, Overflow);
i
}
Err(_) => err!(Overflow),
};
if whole_frac {
acc += 1;
err!(acc > max_abs_int, Overflow);
}
let signed = if neg {
acc.wrapping_neg() as i128
} else {
acc as i128
};
Ok(signed << (<i128>::NBITS - nbits))
}
impl_from_str! { FixedU128, U128, from_str_u128 }
fn from_str_u128(s: &str, int_nbits: u32, frac_nbits: u32) -> Result<u128, ParseFixedError> {
let Parse { int, frac, .. } = parse(s, false)?;
let (frac, whole_frac) = match get_frac128(frac, frac_nbits) {
Some(frac) => (frac, false),
None => (0, true),
};
let int = get_int_u128(int, int_nbits, whole_frac)?;
Ok(int | frac)
}
fn get_int_u128(int: &str, nbits: u32, whole_frac: bool) -> Result<u128, ParseFixedError> {
if nbits <= 64 {
return get_int_u64(int, nbits, whole_frac).map(u128::from);
}
let mut int = int;
while int.starts_with('0') {
int = &int[1..];
}
if nbits == 0 {
err!(whole_frac || !int.is_empty(), Overflow);
return Ok(0);
}
let mut acc = match int.parse::<u128>() {
Ok(i) => i,
Err(_) => err!(Overflow),
};
if whole_frac {
acc = match acc.overflowing_add(1) {
(acc, false) => acc,
(_, true) => err!(Overflow),
};
}
Ok(acc << (<u128 as SealedInt>::NBITS - nbits))
}
fn get_frac128(frac: &str, nbits: u32) -> Option<u128> {
if nbits <= 64 {
return get_frac64(frac, nbits).map(u128::from);
} }
if frac.is_empty() { if frac.is_empty() {
return Some(0); return Some(0);
@ -744,26 +677,26 @@ mod tests {
#[test] #[test]
fn check_parse_bounds() { fn check_parse_bounds() {
let Parse { neg, int, frac } = parse("-12.34", true).unwrap(); let Parse { neg, int, frac } = parse("-12.34", true, 10).unwrap();
assert_eq!((neg, int, frac), (true, "12", "34")); assert_eq!((neg, int, frac), (true, "12", "34"));
let Parse { neg, int, frac } = parse("12.", true).unwrap(); let Parse { neg, int, frac } = parse("12.", true, 10).unwrap();
assert_eq!((neg, int, frac), (false, "12", "")); assert_eq!((neg, int, frac), (false, "12", ""));
let Parse { neg, int, frac } = parse("+.34", false).unwrap(); let Parse { neg, int, frac } = parse("+.34", false, 10).unwrap();
assert_eq!((neg, int, frac), (false, "", "34")); assert_eq!((neg, int, frac), (false, "", "34"));
let Parse { neg, int, frac } = parse("0", false).unwrap(); let Parse { neg, int, frac } = parse("0", false, 10).unwrap();
assert_eq!((neg, int, frac), (false, "0", "")); assert_eq!((neg, int, frac), (false, "0", ""));
let ParseFixedError { kind } = parse("0 ", true).unwrap_err(); let ParseFixedError { kind } = parse("0 ", true, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::InvalidDigit); assert_eq!(kind, ParseErrorKind::InvalidDigit);
let ParseFixedError { kind } = parse("+.", true).unwrap_err(); let ParseFixedError { kind } = parse("+.", true, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::NoDigits); assert_eq!(kind, ParseErrorKind::NoDigits);
let ParseFixedError { kind } = parse(".1.", true).unwrap_err(); let ParseFixedError { kind } = parse(".1.", true, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::TooManyPoints); assert_eq!(kind, ParseErrorKind::TooManyPoints);
let ParseFixedError { kind } = parse("1+2", true).unwrap_err(); let ParseFixedError { kind } = parse("1+2", true, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::InvalidDigit); assert_eq!(kind, ParseErrorKind::InvalidDigit);
let ParseFixedError { kind } = parse("1-2", true).unwrap_err(); let ParseFixedError { kind } = parse("1-2", true, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::InvalidDigit); assert_eq!(kind, ParseErrorKind::InvalidDigit);
let ParseFixedError { kind } = parse("-12", false).unwrap_err(); let ParseFixedError { kind } = parse("-12", false, 10).unwrap_err();
assert_eq!(kind, ParseErrorKind::InvalidDigit); assert_eq!(kind, ParseErrorKind::InvalidDigit);
} }