use lexical_core::parse_partial;
use crate::{
Duration, Fraction, TryFromExact, TryMul, UnitRatio,
errors::{CannotRepresentDecimalNumber, NumberParsingError},
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct DecimalNumber {
pub(crate) integer: i64,
pub(crate) fraction: i64,
pub(crate) fractional_digits: u32,
}
impl DecimalNumber {
pub(crate) fn parse_partial(string: &str) -> Result<(Self, &str), NumberParsingError> {
let (integer, parsed_bytes) = parse_partial::<i64>(string.as_bytes())?;
let remainder = string.get(parsed_bytes..).unwrap();
if let Some('.') = remainder.chars().next() {
let remainder = remainder.get(1..).unwrap();
let (fraction, fractional_digits) = parse_partial::<i64>(remainder.as_bytes())?;
let remainder = remainder.get(fractional_digits..).unwrap();
let fractional_digits = fractional_digits
.try_into()
.map_err(|_| NumberParsingError::TooManyFractionalDigits { fractional_digits })?;
if fraction == 0 {
Ok((
DecimalNumber {
integer,
fraction: 0,
fractional_digits: 0,
},
remainder,
))
} else {
Ok((
DecimalNumber {
integer,
fraction,
fractional_digits,
},
remainder,
))
}
} else {
Ok((
DecimalNumber {
integer,
fraction: 0,
fractional_digits: 0,
},
remainder,
))
}
}
pub(crate) fn is_integer(&self) -> bool {
self.fractional_digits == 0
}
pub(crate) fn convert_period<From, Into, Representation>(
self,
) -> Result<Duration<Representation, Into>, CannotRepresentDecimalNumber>
where
From: UnitRatio,
Into: UnitRatio,
Representation: TryFromExact<i64> + TryMul<Fraction, Output = Representation>,
{
let fraction = Fraction::new(1, 10u128.pow(self.fractional_digits));
let mantissa = if self.integer >= 0 {
10i64.pow(self.fractional_digits) * self.integer + self.fraction
} else {
10i64.pow(self.fractional_digits) * self.integer - self.fraction
};
let uncorrected_duration = Duration::<_, From>::new(mantissa)
.try_into_unit()
.ok_or(CannotRepresentDecimalNumber { number: self })?;
let duration: Duration<Representation, Into> = match uncorrected_duration.try_cast() {
Ok(uncorrected_duration) => uncorrected_duration,
Err(_) => Err(CannotRepresentDecimalNumber { number: self })?,
};
duration
.try_mul(fraction)
.ok_or(CannotRepresentDecimalNumber { number: self })
}
pub(crate) const ZERO: Self = Self {
integer: 0,
fraction: 0,
fractional_digits: 0,
};
}