about summary refs log tree commit diff stats
path: root/crates/core/src/id.rs
blob: a216dd87fe568061899f5de389d2149c97863c56 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Identifier newtype primitives and helpers for defining and working with
//! them.

use core::hash::Hash;

/// Numeric identifier representation.
///
/// Most implementors of this should be defined using the `id` macro for
/// consistency in their implementations.
pub trait Id:
    Copy + Clone + PartialEq + Eq + PartialOrd + Ord + Hash + Send + Sync
{
    /// The backing integer type for this identifier.
    type Integer: Integer;

    /// Instantiate a new identifier from its underlying representation.
    #[must_use]
    fn new(v: Self::Integer) -> Self;
}

//NOTE: we don't use the same [`Integer`] trait in `numerics` here as there
//      is specific behavior desired in an "integer backing an id"---such as
//      checked overflow---that may not be wanted in more general arithmetic
//      operations.

/// Valid integer types backing an [`Id`].
pub trait Integer:
    Copy
    + Clone
    + PartialEq
    + Eq
    + PartialOrd
    + Ord
    + Hash
    + Send
    + Sync
    + sealed::Sealed
{
    /// Zero value of the integer type.
    const ZERO: Self;

    /// Convert this integer into a `usize`.
    fn into_usize(self) -> usize;

    /// Construct an integer from a `usize`.
    ///
    /// Returns `None` if no corresponding integer exists.
    fn from_usize(v: usize) -> Option<Self>;

    /// Returns the successor value of this integer.
    ///
    /// If this would have overflowed, `None` is returned.
    fn increment(self) -> Option<Self>;

    //// Returns the predecessor value of this integer.
    ///
    /// If this would have overflowed, `None` is returned.
    fn decrement(self) -> Option<Self>;
}

macro_rules! integer_impl {
    ($t:ty) => {
        impl sealed::Sealed for $t {}

        impl Integer for $t {
            const ZERO: Self = 0;

            #[allow(trivial_numeric_casts, clippy::cast_possible_truncation)]
            fn into_usize(self) -> usize {
                //NOTE: truncation never happens as `Integer` implementations
                //      are gated behind `target_pointer_width`.
                self as usize
            }

            #[allow(trivial_numeric_casts, clippy::cast_possible_truncation)]
            fn from_usize(v: usize) -> Option<Self> {
                //NOTE: truncation can't happen, see above.
                if v <= Self::MAX as usize {
                    //NOTE: we just checked this exists and doesn't truncate.
                    Some(v as $t)
                } else {
                    None
                }
            }

            fn increment(self) -> Option<Self> {
                self.checked_add(1)
            }

            fn decrement(self) -> Option<Self> {
                self.checked_sub(1)
            }
        }
    };
}

integer_impl!(usize);

#[cfg(target_pointer_width = "64")]
integer_impl!(u64);

#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
integer_impl!(u32);

integer_impl!(u16);
integer_impl!(u8);

mod sealed {
    pub trait Sealed {}
}