use core::num::{NonZeroU128, NonZeroU32};
use crate::{TemporalError, TemporalResult};
use num_traits::float::FloatCore;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct RoundingIncrement(pub(crate) NonZeroU32);
impl Default for RoundingIncrement {
fn default() -> Self {
Self::ONE
}
}
impl TryFrom<f64> for RoundingIncrement {
type Error = TemporalError;
fn try_from(value: f64) -> Result<Self, Self::Error> {
if !value.is_finite() {
return Err(TemporalError::range().with_message("roundingIncrement must be finite"));
}
let integer_increment = FloatCore::trunc(value);
if !(1.0..=1_000_000_000.0).contains(&integer_increment) {
Err(TemporalError::range()
.with_message("roundingIncrement cannot be less that 1 or bigger than 10**9"))
} else {
Ok(Self(
NonZeroU32::new(integer_increment as u32).unwrap_or(NonZeroU32::MIN),
))
}
}
}
impl RoundingIncrement {
pub const ONE: Self = Self(NonZeroU32::MIN);
pub fn try_new(increment: u32) -> TemporalResult<Self> {
if !(1..=1_000_000_000).contains(&increment) {
Err(TemporalError::range()
.with_message("roundingIncrement cannot be less that 1 or bigger than 10**9"))
} else {
Ok(Self(NonZeroU32::new(increment).unwrap_or(NonZeroU32::MIN)))
}
}
pub const fn new_unchecked(increment: NonZeroU32) -> Self {
Self(increment)
}
pub const fn get(self) -> u32 {
self.0.get()
}
pub(crate) fn validate(self, dividend: u64, inclusive: bool) -> TemporalResult<()> {
let max = dividend - u64::from(!inclusive);
let increment = u64::from(self.get());
if increment > max {
return Err(TemporalError::range().with_message("roundingIncrement exceeds maximum"));
}
if dividend.rem_euclid(increment) != 0 {
return Err(TemporalError::range()
.with_message("dividend is not divisible by roundingIncrement"));
}
Ok(())
}
pub(crate) fn as_extended_increment(&self) -> NonZeroU128 {
NonZeroU128::from(self.0)
}
}