1pub mod marker;
45
46mod anonymizable;
47
48pub use anonymizable::AnonymizableId;
49
50use serde::{
51 de::{Deserialize, Deserializer, Error as DeError, Unexpected, Visitor},
52 ser::{Serialize, Serializer},
53};
54use std::{
55 any,
56 cmp::Ordering,
57 fmt::{Debug, Display, Formatter, Result as FmtResult},
58 hash::{Hash, Hasher},
59 marker::PhantomData,
60 num::{NonZero, ParseIntError, TryFromIntError},
61 str::FromStr,
62};
63
64#[repr(transparent)]
79pub struct Id<T> {
80 phantom: PhantomData<fn(T) -> T>,
81 value: NonZero<u64>,
82}
83
84impl<T> Id<T> {
85 const fn from_nonzero(value: NonZero<u64>) -> Self {
86 Self {
87 phantom: PhantomData,
88 value,
89 }
90 }
91 #[track_caller]
114 pub const fn new(n: u64) -> Self {
115 if let Some(id) = Self::new_checked(n) {
116 id
117 } else {
118 panic!("value is zero");
119 }
120 }
121
122 #[allow(unsafe_code)]
130 pub const unsafe fn new_unchecked(n: u64) -> Self {
131 Self::from_nonzero(unsafe { NonZero::new_unchecked(n) })
132 }
133
134 pub const fn new_checked(n: u64) -> Option<Self> {
147 if let Some(n) = NonZero::new(n) {
148 Some(Self::from_nonzero(n))
149 } else {
150 None
151 }
152 }
153
154 pub const fn get(self) -> u64 {
170 self.value.get()
171 }
172
173 pub const fn into_nonzero(self) -> NonZero<u64> {
188 self.value
189 }
190
191 pub const fn cast<New>(self) -> Id<New> {
209 Id::from_nonzero(self.value)
210 }
211}
212
213impl<T> Clone for Id<T> {
214 fn clone(&self) -> Self {
215 *self
216 }
217}
218
219impl<T> Copy for Id<T> {}
220
221impl<T> Debug for Id<T> {
222 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
223 f.write_str("Id")?;
224 let type_name = any::type_name::<T>();
225
226 if let Some(position) = type_name.rfind("::")
230 && let Some(slice) = type_name.get(position + 2..)
231 {
232 f.write_str("<")?;
233 f.write_str(slice)?;
234 f.write_str(">")?;
235 }
236
237 f.write_str("(")?;
238 Debug::fmt(&self.value, f)?;
239
240 f.write_str(")")
241 }
242}
243
244impl<'de, T> Deserialize<'de> for Id<T> {
245 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
246 struct IdVisitor;
247
248 impl<'de> Visitor<'de> for IdVisitor {
249 type Value = NonZero<u64>;
250
251 fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
252 f.write_str("a discord snowflake")
253 }
254
255 fn visit_u64<E: DeError>(self, value: u64) -> Result<Self::Value, E> {
256 let value = NonZero::new(value).ok_or_else(|| {
257 DeError::invalid_value(Unexpected::Unsigned(value), &"non zero u64")
258 })?;
259
260 Ok(value)
261 }
262
263 fn visit_i64<E: DeError>(self, value: i64) -> Result<Self::Value, E> {
264 let unsigned = u64::try_from(value).map_err(|_| {
265 DeError::invalid_value(Unexpected::Signed(value), &"non zero u64")
266 })?;
267
268 self.visit_u64(unsigned)
269 }
270
271 fn visit_newtype_struct<D: Deserializer<'de>>(
272 self,
273 deserializer: D,
274 ) -> Result<Self::Value, D::Error> {
275 deserializer.deserialize_any(IdVisitor)
276 }
277
278 fn visit_str<E: DeError>(self, v: &str) -> Result<Self::Value, E> {
279 let value = v.parse().map_err(|_| {
280 let unexpected = Unexpected::Str(v);
281
282 DeError::invalid_value(unexpected, &"non zero u64 string")
283 })?;
284
285 self.visit_u64(value)
286 }
287 }
288
289 Ok(deserializer.deserialize_any(IdVisitor)?.into())
290 }
291}
292
293impl<T> Display for Id<T> {
294 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
295 Display::fmt(&self.value.get(), f)
296 }
297}
298
299impl<T> From<Id<T>> for u64 {
300 fn from(id: Id<T>) -> Self {
301 id.get()
302 }
303}
304
305impl<T> From<NonZero<u64>> for Id<T> {
306 fn from(id: NonZero<u64>) -> Self {
307 Self::from_nonzero(id)
308 }
309}
310
311impl<T> From<Id<T>> for NonZero<u64> {
312 fn from(id: Id<T>) -> Self {
313 id.into_nonzero()
314 }
315}
316
317impl<T> FromStr for Id<T> {
318 type Err = ParseIntError;
319
320 fn from_str(s: &str) -> Result<Self, Self::Err> {
321 NonZero::from_str(s).map(Self::from_nonzero)
322 }
323}
324
325impl<T> Eq for Id<T> {}
326
327impl<T> Hash for Id<T> {
328 fn hash<U: Hasher>(&self, state: &mut U) {
329 state.write_u64(self.value.get());
330 }
331}
332
333impl<T> Ord for Id<T> {
334 fn cmp(&self, other: &Self) -> Ordering {
335 self.value.cmp(&other.value)
336 }
337}
338
339impl<T> PartialEq for Id<T> {
340 fn eq(&self, other: &Self) -> bool {
341 self.value == other.value
342 }
343}
344
345impl<T> PartialEq<i64> for Id<T> {
346 fn eq(&self, other: &i64) -> bool {
347 u64::try_from(*other).is_ok_and(|v| v == self.value.get())
348 }
349}
350
351impl<T> PartialEq<Id<T>> for i64 {
352 fn eq(&self, other: &Id<T>) -> bool {
353 u64::try_from(*self).is_ok_and(|v| v == other.value.get())
354 }
355}
356
357impl<T> PartialEq<u64> for Id<T> {
358 fn eq(&self, other: &u64) -> bool {
359 self.value.get() == *other
360 }
361}
362
363impl<T> PartialEq<Id<T>> for u64 {
364 fn eq(&self, other: &Id<T>) -> bool {
365 other.value.get() == *self
366 }
367}
368
369impl<T> PartialOrd for Id<T> {
370 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
371 Some(self.cmp(other))
372 }
373}
374
375impl<T> Serialize for Id<T> {
376 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
377 serializer.serialize_newtype_struct("Id", &self.to_string())
378 }
379}
380
381impl<T> TryFrom<i64> for Id<T> {
382 type Error = TryFromIntError;
383
384 fn try_from(value: i64) -> Result<Self, Self::Error> {
385 let signed_nonzero = NonZero::try_from(value)?;
386 let unsigned_nonzero = NonZero::try_from(signed_nonzero)?;
387
388 Ok(Self::from_nonzero(unsigned_nonzero))
389 }
390}
391
392impl<T> TryFrom<u64> for Id<T> {
393 type Error = TryFromIntError;
394
395 fn try_from(value: u64) -> Result<Self, Self::Error> {
396 let nonzero = NonZero::try_from(value)?;
397
398 Ok(Self::from_nonzero(nonzero))
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use super::{
405 Id,
406 marker::{
407 ApplicationMarker, AttachmentMarker, AuditLogEntryMarker, ChannelMarker, CommandMarker,
408 CommandVersionMarker, EmojiMarker, EntitlementMarker, GenericMarker, GuildMarker,
409 IntegrationMarker, InteractionMarker, MessageMarker, RoleMarker,
410 RoleSubscriptionSkuMarker, SkuMarker, StageMarker, UserMarker, WebhookMarker,
411 },
412 };
413 use serde::{Deserialize, Serialize};
414 use serde_test::Token;
415 use static_assertions::assert_impl_all;
416 use std::{
417 collections::hash_map::DefaultHasher,
418 error::Error,
419 fmt::{Debug, Display},
420 hash::{Hash, Hasher},
421 num::NonZeroU64,
422 str::FromStr,
423 };
424
425 assert_impl_all!(ApplicationMarker: Debug, Send, Sync);
426 assert_impl_all!(AttachmentMarker: Debug, Send, Sync);
427 assert_impl_all!(AuditLogEntryMarker: Debug, Send, Sync);
428 assert_impl_all!(ChannelMarker: Debug, Send, Sync);
429 assert_impl_all!(CommandMarker: Debug, Send, Sync);
430 assert_impl_all!(CommandVersionMarker: Debug, Send, Sync);
431 assert_impl_all!(EmojiMarker: Debug, Send, Sync);
432 assert_impl_all!(EntitlementMarker: Debug, Send, Sync);
433 assert_impl_all!(SkuMarker: Debug, Send, Sync);
434 assert_impl_all!(GenericMarker: Debug, Send, Sync);
435 assert_impl_all!(GuildMarker: Debug, Send, Sync);
436 assert_impl_all!(IntegrationMarker: Debug, Send, Sync);
437 assert_impl_all!(InteractionMarker: Debug, Send, Sync);
438 assert_impl_all!(MessageMarker: Debug, Send, Sync);
439 assert_impl_all!(RoleMarker: Debug, Send, Sync);
440 assert_impl_all!(RoleSubscriptionSkuMarker: Debug, Send, Sync);
441 assert_impl_all!(StageMarker: Debug, Send, Sync);
442 assert_impl_all!(UserMarker: Debug, Send, Sync);
443 assert_impl_all!(WebhookMarker: Debug, Send, Sync);
444 assert_impl_all!(Id<GenericMarker>:
445 Clone, Copy, Debug, Deserialize<'static>, Display, Eq, From<NonZeroU64>,
446 FromStr, Hash, Into<NonZeroU64>, Into<u64>, Ord, PartialEq, PartialEq<i64>, PartialEq<u64>, PartialOrd, Send, Serialize, Sync,
447 TryFrom<i64>, TryFrom<u64>
448 );
449 assert_impl_all!(Id<*const ()>: Send, Sync);
451
452 #[test]
455 fn initializers() -> Result<(), Box<dyn Error>> {
456 assert!(Id::<GenericMarker>::new_checked(0).is_none());
458 assert_eq!(Some(1), Id::<GenericMarker>::new_checked(1).map(Id::get));
459
460 assert_eq!(1, Id::<GenericMarker>::new(1).get());
462
463 assert_eq!(
465 123_u64,
466 Id::<GenericMarker>::from(NonZeroU64::new(123).expect("non zero"))
467 );
468
469 assert_eq!(123_u64, Id::<GenericMarker>::from_str("123")?);
471 assert!(Id::<GenericMarker>::from_str("0").is_err());
472 assert!(Id::<GenericMarker>::from_str("123a").is_err());
473
474 assert!(Id::<GenericMarker>::try_from(-123_i64).is_err());
476 assert!(Id::<GenericMarker>::try_from(0_i64).is_err());
477 assert_eq!(123_u64, Id::<GenericMarker>::try_from(123_i64)?);
478 assert!(Id::<GenericMarker>::try_from(0_u64).is_err());
479 assert_eq!(123_u64, Id::<GenericMarker>::try_from(123_u64)?);
480
481 Ok(())
482 }
483
484 #[test]
486 fn conversions() {
487 assert_eq!(1, u64::from(Id::<GenericMarker>::new(1)));
489 assert_eq!(
490 NonZeroU64::new(1).expect("non zero"),
491 NonZeroU64::from(Id::<GenericMarker>::new(1))
492 );
493 }
494
495 #[should_panic(expected = "value is zero")]
497 #[test]
498 const fn test_new_checked_zero() {
499 _ = Id::<GenericMarker>::new(0);
500 }
501
502 #[test]
504 fn cast() {
505 let id = Id::<GenericMarker>::new(123);
506 assert_eq!(123_u64, id.cast::<RoleMarker>());
507 }
508
509 #[test]
511 fn debug() {
512 let id = Id::<RoleMarker>::new(114_941_315_417_899_012);
513
514 assert_eq!("Id<RoleMarker>(114941315417899012)", format!("{id:?}"));
515 }
516
517 #[test]
519 fn display() {
520 let id = Id::<GenericMarker>::new(114_941_315_417_899_012);
521
522 assert_eq!("114941315417899012", id.to_string());
523 }
524
525 #[test]
527 fn hash() {
528 let id = Id::<GenericMarker>::new(123);
529
530 let mut id_hasher = DefaultHasher::new();
531 id.hash(&mut id_hasher);
532
533 let mut value_hasher = DefaultHasher::new();
534 123_u64.hash(&mut value_hasher);
535
536 assert_eq!(id_hasher.finish(), value_hasher.finish());
537 }
538
539 #[test]
541 fn ordering() {
542 let lesser = Id::<GenericMarker>::new(911_638_235_594_244_096);
543 let center = Id::<GenericMarker>::new(911_638_263_322_800_208);
544 let greater = Id::<GenericMarker>::new(911_638_287_939_166_208);
545
546 assert!(center.cmp(&greater).is_lt());
547 assert!(center.cmp(¢er).is_eq());
548 assert!(center.cmp(&lesser).is_gt());
549 }
550
551 #[allow(clippy::too_many_lines)]
552 #[test]
553 fn serde() {
554 serde_test::assert_tokens(
555 &Id::<ApplicationMarker>::new(114_941_315_417_899_012),
556 &[
557 Token::NewtypeStruct { name: "Id" },
558 Token::Str("114941315417899012"),
559 ],
560 );
561 serde_test::assert_de_tokens(
562 &Id::<ApplicationMarker>::new(114_941_315_417_899_012),
563 &[
564 Token::NewtypeStruct { name: "Id" },
565 Token::U64(114_941_315_417_899_012),
566 ],
567 );
568 serde_test::assert_tokens(
569 &Id::<AttachmentMarker>::new(114_941_315_417_899_012),
570 &[
571 Token::NewtypeStruct { name: "Id" },
572 Token::Str("114941315417899012"),
573 ],
574 );
575 serde_test::assert_de_tokens(
576 &Id::<AttachmentMarker>::new(114_941_315_417_899_012),
577 &[
578 Token::NewtypeStruct { name: "Id" },
579 Token::U64(114_941_315_417_899_012),
580 ],
581 );
582 serde_test::assert_tokens(
583 &Id::<AuditLogEntryMarker>::new(114_941_315_417_899_012),
584 &[
585 Token::NewtypeStruct { name: "Id" },
586 Token::Str("114941315417899012"),
587 ],
588 );
589 serde_test::assert_de_tokens(
590 &Id::<AuditLogEntryMarker>::new(114_941_315_417_899_012),
591 &[
592 Token::NewtypeStruct { name: "Id" },
593 Token::U64(114_941_315_417_899_012),
594 ],
595 );
596 serde_test::assert_tokens(
597 &Id::<ChannelMarker>::new(114_941_315_417_899_012),
598 &[
599 Token::NewtypeStruct { name: "Id" },
600 Token::Str("114941315417899012"),
601 ],
602 );
603 serde_test::assert_de_tokens(
604 &Id::<ChannelMarker>::new(114_941_315_417_899_012),
605 &[
606 Token::NewtypeStruct { name: "Id" },
607 Token::U64(114_941_315_417_899_012),
608 ],
609 );
610 serde_test::assert_tokens(
611 &Id::<CommandMarker>::new(114_941_315_417_899_012),
612 &[
613 Token::NewtypeStruct { name: "Id" },
614 Token::Str("114941315417899012"),
615 ],
616 );
617 serde_test::assert_de_tokens(
618 &Id::<CommandMarker>::new(114_941_315_417_899_012),
619 &[
620 Token::NewtypeStruct { name: "Id" },
621 Token::U64(114_941_315_417_899_012),
622 ],
623 );
624 serde_test::assert_tokens(
625 &Id::<CommandVersionMarker>::new(114_941_315_417_899_012),
626 &[
627 Token::NewtypeStruct { name: "Id" },
628 Token::Str("114941315417899012"),
629 ],
630 );
631 serde_test::assert_de_tokens(
632 &Id::<CommandVersionMarker>::new(114_941_315_417_899_012),
633 &[
634 Token::NewtypeStruct { name: "Id" },
635 Token::U64(114_941_315_417_899_012),
636 ],
637 );
638 serde_test::assert_tokens(
639 &Id::<EmojiMarker>::new(114_941_315_417_899_012),
640 &[
641 Token::NewtypeStruct { name: "Id" },
642 Token::Str("114941315417899012"),
643 ],
644 );
645 serde_test::assert_de_tokens(
646 &Id::<EmojiMarker>::new(114_941_315_417_899_012),
647 &[
648 Token::NewtypeStruct { name: "Id" },
649 Token::U64(114_941_315_417_899_012),
650 ],
651 );
652 serde_test::assert_tokens(
653 &Id::<EntitlementMarker>::new(114_941_315_417_899_012),
654 &[
655 Token::NewtypeStruct { name: "Id" },
656 Token::Str("114941315417899012"),
657 ],
658 );
659 serde_test::assert_de_tokens(
660 &Id::<EntitlementMarker>::new(114_941_315_417_899_012),
661 &[
662 Token::NewtypeStruct { name: "Id" },
663 Token::Str("114941315417899012"),
664 ],
665 );
666 serde_test::assert_tokens(
667 &Id::<SkuMarker>::new(114_941_315_417_899_012),
668 &[
669 Token::NewtypeStruct { name: "Id" },
670 Token::Str("114941315417899012"),
671 ],
672 );
673 serde_test::assert_de_tokens(
674 &Id::<SkuMarker>::new(114_941_315_417_899_012),
675 &[
676 Token::NewtypeStruct { name: "Id" },
677 Token::Str("114941315417899012"),
678 ],
679 );
680 serde_test::assert_tokens(
681 &Id::<GenericMarker>::new(114_941_315_417_899_012),
682 &[
683 Token::NewtypeStruct { name: "Id" },
684 Token::Str("114941315417899012"),
685 ],
686 );
687 serde_test::assert_de_tokens(
688 &Id::<GenericMarker>::new(114_941_315_417_899_012),
689 &[
690 Token::NewtypeStruct { name: "Id" },
691 Token::U64(114_941_315_417_899_012),
692 ],
693 );
694 serde_test::assert_tokens(
695 &Id::<GuildMarker>::new(114_941_315_417_899_012),
696 &[
697 Token::NewtypeStruct { name: "Id" },
698 Token::Str("114941315417899012"),
699 ],
700 );
701 serde_test::assert_de_tokens(
702 &Id::<GuildMarker>::new(114_941_315_417_899_012),
703 &[
704 Token::NewtypeStruct { name: "Id" },
705 Token::U64(114_941_315_417_899_012),
706 ],
707 );
708 serde_test::assert_tokens(
709 &Id::<IntegrationMarker>::new(114_941_315_417_899_012),
710 &[
711 Token::NewtypeStruct { name: "Id" },
712 Token::Str("114941315417899012"),
713 ],
714 );
715 serde_test::assert_de_tokens(
716 &Id::<IntegrationMarker>::new(114_941_315_417_899_012),
717 &[
718 Token::NewtypeStruct { name: "Id" },
719 Token::U64(114_941_315_417_899_012),
720 ],
721 );
722 serde_test::assert_tokens(
723 &Id::<InteractionMarker>::new(114_941_315_417_899_012),
724 &[
725 Token::NewtypeStruct { name: "Id" },
726 Token::Str("114941315417899012"),
727 ],
728 );
729 serde_test::assert_de_tokens(
730 &Id::<InteractionMarker>::new(114_941_315_417_899_012),
731 &[
732 Token::NewtypeStruct { name: "Id" },
733 Token::U64(114_941_315_417_899_012),
734 ],
735 );
736 serde_test::assert_tokens(
737 &Id::<MessageMarker>::new(114_941_315_417_899_012),
738 &[
739 Token::NewtypeStruct { name: "Id" },
740 Token::Str("114941315417899012"),
741 ],
742 );
743 serde_test::assert_de_tokens(
744 &Id::<MessageMarker>::new(114_941_315_417_899_012),
745 &[
746 Token::NewtypeStruct { name: "Id" },
747 Token::U64(114_941_315_417_899_012),
748 ],
749 );
750 serde_test::assert_tokens(
751 &Id::<RoleMarker>::new(114_941_315_417_899_012),
752 &[
753 Token::NewtypeStruct { name: "Id" },
754 Token::Str("114941315417899012"),
755 ],
756 );
757 serde_test::assert_de_tokens(
758 &Id::<RoleMarker>::new(114_941_315_417_899_012),
759 &[
760 Token::NewtypeStruct { name: "Id" },
761 Token::U64(114_941_315_417_899_012),
762 ],
763 );
764 serde_test::assert_tokens(
765 &Id::<RoleSubscriptionSkuMarker>::new(114_941_315_417_899_012),
766 &[
767 Token::NewtypeStruct { name: "Id" },
768 Token::Str("114941315417899012"),
769 ],
770 );
771 serde_test::assert_tokens(
772 &Id::<StageMarker>::new(114_941_315_417_899_012),
773 &[
774 Token::NewtypeStruct { name: "Id" },
775 Token::Str("114941315417899012"),
776 ],
777 );
778 serde_test::assert_de_tokens(
779 &Id::<StageMarker>::new(114_941_315_417_899_012),
780 &[
781 Token::NewtypeStruct { name: "Id" },
782 Token::U64(114_941_315_417_899_012),
783 ],
784 );
785 serde_test::assert_tokens(
786 &Id::<UserMarker>::new(114_941_315_417_899_012),
787 &[
788 Token::NewtypeStruct { name: "Id" },
789 Token::Str("114941315417899012"),
790 ],
791 );
792 serde_test::assert_de_tokens(
793 &Id::<UserMarker>::new(114_941_315_417_899_012),
794 &[
795 Token::NewtypeStruct { name: "Id" },
796 Token::U64(114_941_315_417_899_012),
797 ],
798 );
799 serde_test::assert_tokens(
800 &Id::<WebhookMarker>::new(114_941_315_417_899_012),
801 &[
802 Token::NewtypeStruct { name: "Id" },
803 Token::Str("114941315417899012"),
804 ],
805 );
806 serde_test::assert_de_tokens(
807 &Id::<WebhookMarker>::new(114_941_315_417_899_012),
808 &[
809 Token::NewtypeStruct { name: "Id" },
810 Token::U64(114_941_315_417_899_012),
811 ],
812 );
813 serde_test::assert_de_tokens(
814 &Id::<WebhookMarker>::new(114_941_315_417_899_012),
815 &[
816 Token::NewtypeStruct { name: "Id" },
817 Token::I64(114_941_315_417_899_012),
818 ],
819 );
820 }
821}