use core::isize;
use core::num::ParseIntError;
use core::str::FromStr;
use displaydoc::Display;
use fixed_decimal::FixedDecimal;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[allow(clippy::exhaustive_structs)] pub struct PluralOperands {
pub(crate) i: u64,
pub(crate) v: usize,
pub(crate) w: usize,
pub(crate) f: u64,
pub(crate) t: u64,
pub(crate) c: usize,
}
#[derive(Display, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum OperandsError {
#[displaydoc("Input to the Operands parsing was empty")]
Empty,
#[displaydoc("Input to the Operands parsing was invalid")]
Invalid,
}
#[cfg(feature = "std")]
impl std::error::Error for OperandsError {}
impl From<ParseIntError> for OperandsError {
fn from(_: ParseIntError) -> Self {
Self::Invalid
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for OperandsError {
fn from(_: std::io::Error) -> Self {
Self::Invalid
}
}
fn get_exponent(input: &str) -> Result<(&str, usize), OperandsError> {
if let Some((base, exponent)) = input.split_once('e') {
Ok((base, exponent.parse()?))
} else {
Ok((input, 0))
}
}
impl FromStr for PluralOperands {
type Err = OperandsError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
if input.is_empty() {
return Err(OperandsError::Empty);
}
let abs_str = input.strip_prefix('-').unwrap_or(input);
let (
integer_digits,
num_fraction_digits0,
num_fraction_digits,
fraction_digits0,
fraction_digits,
exponent,
) = if let Some((int_str, rest)) = abs_str.split_once('.') {
let (dec_str, exponent) = get_exponent(rest)?;
let integer_digits = u64::from_str(int_str)?;
let dec_str_no_zeros = dec_str.trim_end_matches('0');
let num_fraction_digits0 = dec_str.len() as usize;
let num_fraction_digits = dec_str_no_zeros.len() as usize;
let fraction_digits0 = u64::from_str(dec_str)?;
let fraction_digits =
if num_fraction_digits == 0 || num_fraction_digits == num_fraction_digits0 {
fraction_digits0
} else {
u64::from_str(dec_str_no_zeros)?
};
(
integer_digits,
num_fraction_digits0,
num_fraction_digits,
fraction_digits0,
fraction_digits,
exponent,
)
} else {
let (abs_str, exponent) = get_exponent(abs_str)?;
let integer_digits = u64::from_str(abs_str)?;
(integer_digits, 0, 0, 0, 0, exponent)
};
Ok(Self {
i: integer_digits,
v: num_fraction_digits0,
w: num_fraction_digits,
f: fraction_digits0,
t: fraction_digits,
c: exponent,
})
}
}
macro_rules! impl_integer_type {
($ty:ident) => {
impl From<$ty> for PluralOperands {
#[inline]
fn from(input: $ty) -> Self {
Self {
i: input as u64,
v: 0,
w: 0,
f: 0,
t: 0,
c: 0,
}
}
}
};
($($ty:ident)+) => {
$(impl_integer_type!($ty);)+
};
}
macro_rules! impl_signed_integer_type {
($ty:ident) => {
impl From<$ty> for PluralOperands {
#[inline]
fn from(input: $ty) -> Self {
input.unsigned_abs().into()
}
}
};
($($ty:ident)+) => {
$(impl_signed_integer_type!($ty);)+
};
}
impl_integer_type!(u8 u16 u32 u64 u128 usize);
impl_signed_integer_type!(i8 i16 i32 i64 i128 isize);
impl From<&FixedDecimal> for PluralOperands {
fn from(dec: &FixedDecimal) -> Self {
let mag_range = dec.magnitude_range();
let mag_high = core::cmp::min(17, *mag_range.end());
let mag_low = core::cmp::max(-18, *mag_range.start());
let mut i: u64 = 0;
for magnitude in (0..=mag_high).rev() {
i *= 10;
i += dec.digit_at(magnitude) as u64;
}
let mut f: u64 = 0;
let mut t: u64 = 0;
let mut w: usize = 0;
for magnitude in (mag_low..=-1).rev() {
let digit = dec.digit_at(magnitude) as u64;
f *= 10;
f += digit;
if digit != 0 {
t = f;
w = (-magnitude) as usize;
}
}
Self {
i,
v: (-mag_low) as usize,
w,
f,
t,
c: 0,
}
}
}