#[cfg(feature="std")]
use {
alloc::string::String,
core::str::FromStr,
crate::{Error, Number, Result},
};
#[cfg(feature="std")]
const MAX_I8_LEN: usize = 4;
#[cfg(feature="std")]
const MAX_I16_LEN: usize = 6;
#[cfg(feature="std")]
const MAX_I32_LEN: usize = 11;
#[cfg(feature="std")]
const MAX_I64_LEN: usize = 21;
#[cfg(feature="std")]
const MAX_I128_LEN: usize = 40;
#[cfg(feature="std")]
const MAX_INTEGER_LEN: usize = MAX_I128_LEN;
#[cfg(feature="std")]
const MAX_FLOAT_LEN: usize = 100;
#[cfg(feature="std")]
#[test]
fn tests() {
macro_rules! test { ($($constant: ident, $ty: ty,)+) => {
$(
assert_eq!($constant, (<$ty>::max_value() as f64).log10().ceil() as usize + 1);
)+
}}
test!(
MAX_I8_LEN, u8,
MAX_I16_LEN, u16,
MAX_I32_LEN, u32,
MAX_I64_LEN, u64,
MAX_I128_LEN, u128,
);
}
#[cfg(feature="std")]
#[derive(Debug)]
pub struct NumberParser {
s: String,
is_integer: bool,
}
#[cfg(feature="std")]
impl NumberParser {
pub fn new() -> Self {
Self {
s: String::with_capacity(MAX_INTEGER_LEN.max(MAX_FLOAT_LEN)),
is_integer: true,
}
}
pub fn add(&mut self, b: &u8) -> Result<()> {
match self.is_integer {
true => if self.s.len() >= MAX_INTEGER_LEN {
return Err(Error::from(__!("Integer too large")));
},
false => if self.s.len() >= MAX_FLOAT_LEN {
return Err(Error::from(__!("Float too long")));
},
};
let c = char::from(*b);
match c {
'-' | '+' => match self.s.is_empty() || self.s.ends_with(|c| c == 'e' || c == 'E') {
true => self.s.push(c),
false => return Err(Error::from(__!("Invalid sign: {s}{c}", s=self.s, c=c))),
},
'e' | 'E' | '.' => {
if self.is_integer {
self.is_integer = false;
}
self.s.push(c);
},
'0'..='9' => self.s.push(c),
_ => return Err(Error::from(__!("Invalid number: {s}{c}", s=self.s, c=c))),
};
Ok(())
}
pub fn parse(&self) -> Result<Number> {
if self.s.is_empty() {
return Err(Error::from(__!("Number is empty")));
}
fn parse_int<T, U>(s: &str) -> Result<Number> where T: Into<Number> + FromStr, U: Into<Number> + FromStr {
match T::from_str(s) {
Ok(n) => Ok(n.into()),
Err(_) => match U::from_str(s) {
Ok(n) => Ok(n.into()),
Err(_) => Err(Error::from(__!("Invalid integer: {:?}", s))),
},
}
}
match self.is_integer {
true => {
let is_negative = self.s.starts_with(|c| c == '-');
return match self.s.len() {
0..=MAX_I8_LEN => match is_negative {
true => parse_int::<i8, i16>(&self.s),
false => parse_int::<u8, u16>(&self.s),
},
MAX_I8_LEN..=MAX_I16_LEN => match is_negative {
true => parse_int::<i16, i32>(&self.s),
false => parse_int::<u16, u32>(&self.s),
},
MAX_I16_LEN..=MAX_I32_LEN => match is_negative {
true => parse_int::<i32, i64>(&self.s),
false => parse_int::<u32, u64>(&self.s),
},
MAX_I32_LEN..=MAX_I64_LEN => match is_negative {
true => parse_int::<i64, i128>(&self.s),
false => parse_int::<u64, u128>(&self.s),
},
_ => match is_negative {
true => i128::from_str(&self.s).map(|i| Number::from(i)).map_err(|_| Error::from(__!("Invalid integer: {:?}", self.s))),
false => u128::from_str(&self.s).map(|u| Number::from(u)).map_err(|_| Error::from(__!("Invalid integer: {:?}", self.s))),
},
};
},
false => f64::from_str(&self.s).map(|f| f.into()).map_err(|e| Error::from(__!("{}", e))),
}
}
pub fn reset(&mut self) {
self.s.clear();
self.is_integer = true;
}
}