tuwunel_core/matrix/pdu/
count.rs1#![expect(
2 clippy::cast_possible_wrap,
3 clippy::cast_sign_loss,
4 clippy::as_conversions
5)]
6
7use std::{cmp::Ordering, fmt, fmt::Display, str::FromStr};
8
9use ruma::api::Direction;
10
11use crate::{Error, Result, err};
12
13#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
14pub enum Count {
16 Normal(u64),
17 Backfilled(i64),
18}
19
20impl Count {
21 #[inline]
22 #[must_use]
23 pub fn to_be_bytes(self) -> [u8; size_of::<u64>()] { self.into_unsigned().to_be_bytes() }
24
25 #[inline]
26 #[must_use]
27 pub fn from_unsigned(unsigned: u64) -> Self { Self::from_signed(unsigned as i64) }
28
29 #[inline]
30 #[must_use]
31 pub fn from_signed(signed: i64) -> Self {
32 match signed {
33 | i64::MIN..=0 => Self::Backfilled(signed),
34 | _ => Self::Normal(signed as u64),
35 }
36 }
37
38 #[inline]
39 #[must_use]
40 pub fn into_unsigned(self) -> u64 {
41 self.debug_assert_valid();
42 match self {
43 | Self::Normal(i) => i,
44 | Self::Backfilled(i) => i as u64,
45 }
46 }
47
48 #[inline]
49 #[must_use]
50 pub fn into_signed(self) -> i64 {
51 self.debug_assert_valid();
52 match self {
53 | Self::Normal(i) => i as i64,
54 | Self::Backfilled(i) => i,
55 }
56 }
57
58 #[inline]
59 #[must_use]
60 pub fn into_normal(self) -> Self {
61 self.debug_assert_valid();
62 match self {
63 | Self::Normal(i) => Self::Normal(i),
64 | Self::Backfilled(_) => Self::Normal(0),
65 }
66 }
67
68 #[inline]
69 pub fn checked_inc(self, dir: Direction) -> Result<Self, Error> {
70 match dir {
71 | Direction::Forward => self.checked_add(1),
72 | Direction::Backward => self.checked_sub(1),
73 }
74 }
75
76 #[inline]
77 pub fn checked_add(self, add: u64) -> Result<Self, Error> {
78 Ok(match self {
79 | Self::Normal(i) => Self::Normal(
80 i.checked_add(add)
81 .ok_or_else(|| err!(Arithmetic("Count::Normal overflow")))?,
82 ),
83 | Self::Backfilled(i) => Self::Backfilled(
84 i.checked_add(add as i64)
85 .ok_or_else(|| err!(Arithmetic("Count::Backfilled overflow")))?,
86 ),
87 })
88 }
89
90 #[inline]
91 pub fn checked_sub(self, sub: u64) -> Result<Self, Error> {
92 Ok(match self {
93 | Self::Normal(i) => Self::Normal(
94 i.checked_sub(sub)
95 .ok_or_else(|| err!(Arithmetic("Count::Normal underflow")))?,
96 ),
97 | Self::Backfilled(i) => Self::Backfilled(
98 i.checked_sub(sub as i64)
99 .ok_or_else(|| err!(Arithmetic("Count::Backfilled underflow")))?,
100 ),
101 })
102 }
103
104 #[inline]
105 #[must_use]
106 pub fn saturating_inc(self, dir: Direction) -> Self {
107 match dir {
108 | Direction::Forward => self.saturating_add(1),
109 | Direction::Backward => self.saturating_sub(1),
110 }
111 }
112
113 #[inline]
114 #[must_use]
115 pub fn saturating_add(self, add: u64) -> Self {
116 match self {
117 | Self::Normal(i) => Self::Normal(i.saturating_add(add)),
118 | Self::Backfilled(i) => Self::Backfilled(i.saturating_add(add as i64)),
119 }
120 }
121
122 #[inline]
123 #[must_use]
124 pub fn saturating_sub(self, sub: u64) -> Self {
125 match self {
126 | Self::Normal(i) => Self::Normal(i.saturating_sub(sub)),
127 | Self::Backfilled(i) => Self::Backfilled(i.saturating_sub(sub as i64)),
128 }
129 }
130
131 #[inline]
132 #[must_use]
133 pub const fn min() -> Self { Self::Backfilled(i64::MIN) }
134
135 #[inline]
136 #[must_use]
137 pub const fn max() -> Self { Self::Normal(i64::MAX as u64) }
138
139 #[inline]
140 pub(crate) fn debug_assert_valid(&self) {
141 if let Self::Backfilled(i) = self {
142 debug_assert!(*i <= 0, "Backfilled sequence must be negative");
143 }
144 }
145}
146
147impl Display for Count {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
149 self.debug_assert_valid();
150 match self {
151 | Self::Normal(i) => write!(f, "{i}"),
152 | Self::Backfilled(i) => write!(f, "{i}"),
153 }
154 }
155}
156
157impl From<i64> for Count {
158 #[inline]
159 fn from(signed: i64) -> Self { Self::from_signed(signed) }
160}
161
162impl From<u64> for Count {
163 #[inline]
164 fn from(unsigned: u64) -> Self { Self::from_unsigned(unsigned) }
165}
166
167impl FromStr for Count {
168 type Err = Error;
169
170 fn from_str(token: &str) -> Result<Self, Self::Err> { Ok(Self::from_signed(token.parse()?)) }
171}
172
173impl PartialOrd for Count {
174 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
175}
176
177impl Ord for Count {
178 fn cmp(&self, other: &Self) -> Ordering { self.into_signed().cmp(&other.into_signed()) }
179}
180
181impl Default for Count {
182 fn default() -> Self { Self::Normal(0) }
183}