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