1use std::marker::PhantomData;
15
16pub struct Id<T> {
23 index: u32,
24 _marker: PhantomData<fn() -> T>,
25}
26
27impl<T> std::fmt::Debug for Id<T> {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.debug_tuple("Id").field(&self.index).finish()
30 }
31}
32
33impl<T> Clone for Id<T> {
34 fn clone(&self) -> Self {
35 *self
36 }
37}
38
39impl<T> Copy for Id<T> {}
40
41impl<T> PartialEq for Id<T> {
42 fn eq(&self, other: &Self) -> bool {
43 self.index == other.index
44 }
45}
46
47impl<T> Eq for Id<T> {}
48
49impl<T> PartialOrd for Id<T> {
50 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
51 Some(self.cmp(other))
52 }
53}
54
55impl<T> Ord for Id<T> {
56 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
57 self.index.cmp(&other.index)
58 }
59}
60
61impl<T> std::hash::Hash for Id<T> {
62 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
63 self.index.hash(state);
64 }
65}
66
67impl<T> Id<T> {
68 pub const fn from_index(index: usize) -> Self {
75 Id {
76 index: index as u32,
77 _marker: PhantomData,
78 }
79 }
80
81 pub const fn index(self) -> usize {
83 self.index as usize
84 }
85}
86
87impl<T> std::fmt::Display for Id<T> {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(f, "{}", self.index)
90 }
91}
92
93pub trait IdLike {
95 fn index(self) -> usize;
97}
98
99impl<T> IdLike for Id<T> {
100 fn index(self) -> usize {
101 Id::index(self)
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::Id;
108
109 struct File;
110 struct Rule;
111
112 #[test]
113 fn ids_are_cheap_copyable_and_comparable() {
114 let a = Id::<File>::from_index(3);
115 let b = Id::<File>::from_index(3);
116 let c = Id::<File>::from_index(4);
117 assert_eq!(a, b);
118 assert_ne!(a, c);
119 assert_eq!(a.index(), 3);
120 assert!(a < c);
121 assert_eq!(std::mem::size_of::<Id<File>>(), 4);
122 }
123
124 #[test]
125 fn distinct_id_types_are_distinct() {
126 let _file: Id<File> = Id::from_index(0);
130 let _rule: Id<Rule> = Id::from_index(0);
131 let _ = _file;
132 let _ = _rule;
133 }
134
135 #[test]
136 fn ids_survive_hashing() {
137 use std::collections::HashSet;
138 let mut set = HashSet::new();
139 set.insert(Id::<File>::from_index(7));
140 assert!(set.contains(&Id::<File>::from_index(7)));
141 }
142}