intid_core/
uint.rs

1//! Defines the [`UnsignedPrimInt`] trait and its generic operations.
2//!
3//! These are module functions rather than trait functions
4//! to avoid polluting the primitive integer namespaces.
5
6use core::fmt::{Debug, Display, Formatter};
7use core::hash::Hash;
8
9mod sealed;
10
11maybe_trait_bound!(
12    MaybeNumTrait,
13    cfg(feature = "num-traits"),
14    num_traits::PrimInt
15);
16maybe_trait_bound!(MaybePod, cfg(feature = "bytemuck"), bytemuck::Pod);
17maybe_trait_bound!(
18    MaybeContiguous,
19    cfg(feature = "bytemuck"),
20    bytemuck::Contiguous
21);
22#[doc(hidden)]
23pub trait ConvertPrimInts:
24    From<u8>
25    + TryFrom<u8>
26    + TryFrom<u16>
27    + TryFrom<u32>
28    + TryFrom<u64>
29    + TryFrom<u128>
30    + TryFrom<usize>
31    + TryInto<u8>
32    + TryInto<u16>
33    + TryFrom<u32>
34    + TryFrom<u64>
35    + TryInto<u128>
36    + TryInto<usize>
37{
38}
39
40/// An unsigned primitive integer.
41///
42/// Most methods in this trait are only available through the [`intid::uint`](crate::uint) module
43/// in order to avoid conflict with inherit implementations and other traits.
44/// You can get access to more functionality by enabling the `num-traits` or `bytemuck` features,
45/// which will add [`num_traits::PrimInt`] and [`bytemuck::Pod`] bounds respectively.
46pub trait UnsignedPrimInt:
47    Eq
48    + Hash
49    + Ord
50    + Copy
51    + Default
52    + Debug
53    + Display
54    + ConvertPrimInts
55    + sealed::PrivateUnsignedInt
56    + MaybeNumTrait
57    + MaybePod
58    + MaybeContiguous
59{
60}
61
62/// Cast from one [`UnsignedPrimInt`] into another,
63/// returning `None` if there is overflow.
64#[inline]
65pub fn checked_cast<T: UnsignedPrimInt, U>(value: T) -> Option<T> {
66    sealed::PrivateUnsignedInt::checked_cast(value)
67}
68
69/// Add the specified value to the integer,
70/// returning `None` if overflow occurs.
71#[inline]
72pub fn checked_add<T: UnsignedPrimInt>(left: T, right: T) -> Option<T> {
73    sealed::PrivateUnsignedInt::checked_add(left, right)
74}
75
76/// Subtract the specified value from the integer,
77/// returning `None` if overflow occurs.
78#[inline]
79pub fn checked_sub<T: UnsignedPrimInt>(left: T, right: T) -> Option<T> {
80    sealed::PrivateUnsignedInt::checked_sub(left, right)
81}
82
83/// Convert a primitive integer to a [`usize`],
84/// returning `None` if overflow occurs.
85#[inline]
86pub fn to_usize_checked<T: UnsignedPrimInt>(val: T) -> Option<usize> {
87    T::to_usize_checked(val)
88}
89
90/// Convert a primitive integer to a [`usize`],
91/// wrapping around on overflow.
92#[inline]
93pub fn to_usize_wrapping<T: UnsignedPrimInt>(val: T) -> usize {
94    T::to_usize_wrapping(val)
95}
96
97/// Convert a primitive integer to a [`usize`],
98/// returning `None` if overflow occurs.
99#[inline]
100pub fn from_usize_checked<T: UnsignedPrimInt>(val: usize) -> Option<T> {
101    T::from_usize_checked(val)
102}
103
104/// Convert a primitive integer to a [`usize`],
105/// wrapping around if overflow occurs.
106#[inline]
107pub fn from_usize_wrapping<T: UnsignedPrimInt>(val: usize) -> T {
108    T::from_usize_wrapping(val)
109}
110
111/// Determine the zero value of the specified `UnsignedPrimInt`.
112///
113/// This function always succeeds (a `NonZero` is not a primitive integer)
114#[inline]
115pub const fn zero<T: UnsignedPrimInt>() -> T {
116    T::ZERO
117}
118
119/// Determine the one value of the specified `UnsignedPrimInt`.
120#[inline]
121pub const fn one<T: UnsignedPrimInt>() -> T {
122    T::ONE
123}
124
125/// Determine the maximum value of the specified [`UnsignedPrimInt`].
126#[inline]
127pub const fn max_value<T: UnsignedPrimInt>() -> T {
128    T::MAX
129}
130
131/// Attempt to describe the specified [`UnsignedPrimInt`]
132/// in a format suitable for debugging or panic messages.
133///
134/// This differs from the standard `Display` and `Debug` implementation,
135/// because `T::MAX` is special-cased.
136///
137/// *WARNING*: This representation may change without warning in the future,
138/// so the exact representation should not be relied upon.
139///
140/// ## Examples
141/// ```
142/// use intid_core::uint::debug_desc;
143/// assert_eq!(
144///     debug_desc(3u32).to_string(),
145///     "3"
146/// );
147/// assert_eq!(
148///     debug_desc(u32::MAX).to_string(),
149///     "u32::MAX"
150/// )
151/// ```
152#[cold]
153pub fn debug_desc<T: UnsignedPrimInt>(value: T) -> DebugDesc<T> {
154    DebugDesc(value)
155}
156
157/// The description of an unsigned integer returned by [`debug_desc`].
158#[derive(Clone)]
159pub struct DebugDesc<T: UnsignedPrimInt>(T);
160impl<T: UnsignedPrimInt> Display for DebugDesc<T> {
161    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
162        if self.0 == T::MAX {
163            f.write_str(T::TYPE_NAME)?;
164            f.write_str("::MAX")
165        } else {
166            <T as Display>::fmt(&self.0, f)
167        }
168    }
169}
170impl<T: UnsignedPrimInt> Debug for DebugDesc<T> {
171    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
172        <Self as Display>::fmt(self, f)
173    }
174}
175
176/// Panic with a message indicating that an ID is not valid.
177///
178/// Used to implement the panic in [`crate::IntegerId::from_int`].
179#[inline(never)]
180#[track_caller]
181#[cold]
182pub(crate) fn invalid_id<T: UnsignedPrimInt>(id: T) -> ! {
183    panic!("Invalid id: {}", debug_desc(id))
184}