twilight_model/gateway/
id.rs1use serde::{Deserialize, Serialize};
2use std::{
3 error::Error,
4 fmt::{Display, Formatter, Result as FmtResult},
5 num::NonZero,
6};
7
8pub struct ShardIdParseError {
9 kind: ShardIdParseErrorType,
10}
11
12impl ShardIdParseError {
13 #[must_use = "retrieving the type has no effect if left unused"]
15 pub const fn kind(&self) -> &ShardIdParseErrorType {
16 &self.kind
17 }
18
19 #[allow(clippy::unused_self)]
21 #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
22 pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
23 None
24 }
25
26 #[must_use = "consuming the error into its parts has no effect if left unused"]
28 pub fn into_parts(self) -> (ShardIdParseErrorType, Option<Box<dyn Error + Send + Sync>>) {
29 (self.kind, None)
30 }
31}
32
33impl Display for ShardIdParseError {
34 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
35 match self.kind {
36 ShardIdParseErrorType::NumberGreaterOrEqualTotal { number, total } => {
37 f.write_str("ShardId's number (")?;
38 Display::fmt(&number, f)?;
39 f.write_str(") was greater or equal to its total (")?;
40 Display::fmt(&total, f)?;
41
42 f.write_str(")")
43 }
44 }
45 }
46}
47
48#[derive(Debug)]
50pub enum ShardIdParseErrorType {
51 NumberGreaterOrEqualTotal {
53 number: u32,
55 total: u32,
57 },
58}
59
60#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
95#[serde(try_from = "[u32; 2]", into = "[u32; 2]")]
96pub struct ShardId {
97 number: u32,
99 total: NonZero<u32>,
101}
102
103impl ShardId {
104 pub const ONE: ShardId = ShardId::new(0, 1);
108
109 #[inline]
131 pub const fn new(number: u32, total: u32) -> Self {
132 assert!(number < total, "number must be less than total");
133 Self {
134 number,
135 total: NonZero::new(total).expect("total is at least 1"),
136 }
137 }
138
139 #[allow(clippy::missing_panics_doc)]
141 #[inline]
142 pub const fn new_checked(number: u32, total: u32) -> Option<Self> {
143 if number < total {
144 Some(Self {
145 number,
146 total: NonZero::new(total).expect("total is at least 1"),
147 })
148 } else {
149 None
150 }
151 }
152
153 pub const fn number(self) -> u32 {
155 self.number
156 }
157
158 pub const fn total(self) -> u32 {
160 self.total.get()
161 }
162}
163
164impl Display for ShardId {
168 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
169 f.debug_list().entries(<[u32; 2]>::from(*self)).finish()
170 }
171}
172
173impl PartialOrd for ShardId {
174 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
175 (self.total == other.total).then(|| self.number.cmp(&other.number))
176 }
177}
178
179impl TryFrom<[u32; 2]> for ShardId {
180 type Error = ShardIdParseError;
181
182 fn try_from([number, total]: [u32; 2]) -> Result<Self, Self::Error> {
183 Self::new_checked(number, total).ok_or(ShardIdParseError {
184 kind: ShardIdParseErrorType::NumberGreaterOrEqualTotal { number, total },
185 })
186 }
187}
188
189impl From<ShardId> for [u32; 2] {
190 fn from(id: ShardId) -> Self {
191 [id.number(), id.total()]
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::ShardId;
198 use serde::{Serialize, de::DeserializeOwned};
199 use serde_test::Token;
200 use static_assertions::{assert_impl_all, const_assert_eq};
201 use std::{fmt::Debug, hash::Hash};
202
203 const_assert_eq!(ShardId::ONE.number(), 0);
204 const_assert_eq!(ShardId::ONE.total(), 1);
205 assert_impl_all!(
206 ShardId: Clone,
207 Copy,
208 Debug,
209 DeserializeOwned,
210 Eq,
211 Hash,
212 PartialEq,
213 PartialOrd,
214 Send,
215 Serialize,
216 Sync
217 );
218
219 #[test]
220 const fn checked_invalid() {
221 assert!(ShardId::new_checked(0, 1).is_some());
222 assert!(ShardId::new_checked(1, 1).is_none());
223 assert!(ShardId::new_checked(2, 1).is_none());
224 assert!(ShardId::new_checked(0, 0).is_none());
225 }
226
227 #[test]
228 const fn getters() {
229 let id = ShardId::new(2, 4);
230
231 assert!(id.number() == 2);
232 assert!(id.total() == 4);
233 }
234
235 #[test]
236 fn serde() {
237 let value = ShardId::new(0, 1);
238
239 serde_test::assert_tokens(
240 &value,
241 &[
242 Token::Tuple { len: 2 },
243 Token::U32(0),
244 Token::U32(1),
245 Token::TupleEnd,
246 ],
247 )
248 }
249
250 #[should_panic(expected = "number must be less than total")]
251 #[test]
252 const fn number_equal() {
253 ShardId::new(1, 1);
254 }
255
256 #[should_panic(expected = "number must be less than total")]
257 #[test]
258 const fn number_greater() {
259 ShardId::new(2, 1);
260 }
261}