174 lines
3.9 KiB
Rust
174 lines
3.9 KiB
Rust
#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::as_conversions)]
|
|
|
|
use std::{cmp::Ordering, fmt, fmt::Display, str::FromStr};
|
|
|
|
use ruma::api::Direction;
|
|
|
|
use crate::{err, Error, Result};
|
|
|
|
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
|
|
pub enum Count {
|
|
Normal(u64),
|
|
Backfilled(i64),
|
|
}
|
|
|
|
impl Count {
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn from_unsigned(unsigned: u64) -> Self { Self::from_signed(unsigned as i64) }
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn from_signed(signed: i64) -> Self {
|
|
match signed {
|
|
i64::MIN..=0 => Self::Backfilled(signed),
|
|
_ => Self::Normal(signed as u64),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn into_unsigned(self) -> u64 {
|
|
self.debug_assert_valid();
|
|
match self {
|
|
Self::Normal(i) => i,
|
|
Self::Backfilled(i) => i as u64,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn into_signed(self) -> i64 {
|
|
self.debug_assert_valid();
|
|
match self {
|
|
Self::Normal(i) => i as i64,
|
|
Self::Backfilled(i) => i,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn into_normal(self) -> Self {
|
|
self.debug_assert_valid();
|
|
match self {
|
|
Self::Normal(i) => Self::Normal(i),
|
|
Self::Backfilled(_) => Self::Normal(0),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn checked_inc(self, dir: Direction) -> Result<Self, Error> {
|
|
match dir {
|
|
Direction::Forward => self.checked_add(1),
|
|
Direction::Backward => self.checked_sub(1),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn checked_add(self, add: u64) -> Result<Self, Error> {
|
|
Ok(match self {
|
|
Self::Normal(i) => Self::Normal(
|
|
i.checked_add(add)
|
|
.ok_or_else(|| err!(Arithmetic("Count::Normal overflow")))?,
|
|
),
|
|
Self::Backfilled(i) => Self::Backfilled(
|
|
i.checked_add(add as i64)
|
|
.ok_or_else(|| err!(Arithmetic("Count::Backfilled overflow")))?,
|
|
),
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
pub fn checked_sub(self, sub: u64) -> Result<Self, Error> {
|
|
Ok(match self {
|
|
Self::Normal(i) => Self::Normal(
|
|
i.checked_sub(sub)
|
|
.ok_or_else(|| err!(Arithmetic("Count::Normal underflow")))?,
|
|
),
|
|
Self::Backfilled(i) => Self::Backfilled(
|
|
i.checked_sub(sub as i64)
|
|
.ok_or_else(|| err!(Arithmetic("Count::Backfilled underflow")))?,
|
|
),
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn saturating_inc(self, dir: Direction) -> Self {
|
|
match dir {
|
|
Direction::Forward => self.saturating_add(1),
|
|
Direction::Backward => self.saturating_sub(1),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn saturating_add(self, add: u64) -> Self {
|
|
match self {
|
|
Self::Normal(i) => Self::Normal(i.saturating_add(add)),
|
|
Self::Backfilled(i) => Self::Backfilled(i.saturating_add(add as i64)),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub fn saturating_sub(self, sub: u64) -> Self {
|
|
match self {
|
|
Self::Normal(i) => Self::Normal(i.saturating_sub(sub)),
|
|
Self::Backfilled(i) => Self::Backfilled(i.saturating_sub(sub as i64)),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn min() -> Self { Self::Backfilled(i64::MIN) }
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub const fn max() -> Self { Self::Normal(i64::MAX as u64) }
|
|
|
|
#[inline]
|
|
pub(crate) fn debug_assert_valid(&self) {
|
|
if let Self::Backfilled(i) = self {
|
|
debug_assert!(*i <= 0, "Backfilled sequence must be negative");
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Count {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
self.debug_assert_valid();
|
|
match self {
|
|
Self::Normal(i) => write!(f, "{i}"),
|
|
Self::Backfilled(i) => write!(f, "{i}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<i64> for Count {
|
|
#[inline]
|
|
fn from(signed: i64) -> Self { Self::from_signed(signed) }
|
|
}
|
|
|
|
impl From<u64> for Count {
|
|
#[inline]
|
|
fn from(unsigned: u64) -> Self { Self::from_unsigned(unsigned) }
|
|
}
|
|
|
|
impl FromStr for Count {
|
|
type Err = Error;
|
|
|
|
fn from_str(token: &str) -> Result<Self, Self::Err> { Ok(Self::from_signed(token.parse()?)) }
|
|
}
|
|
|
|
impl PartialOrd for Count {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
|
}
|
|
|
|
impl Ord for Count {
|
|
fn cmp(&self, other: &Self) -> Ordering { self.into_signed().cmp(&other.into_signed()) }
|
|
}
|
|
|
|
impl Default for Count {
|
|
fn default() -> Self { Self::Normal(0) }
|
|
}
|