1use std::{convert::TryFrom, fmt, hash::{Hash, Hasher}, marker::PhantomData};
4
5#[derive(Eq)]
26pub struct Index<T> {
27 ndx: u32,
28 _marker: PhantomData<T>,
29}
30
31impl<T> Clone for Index<T> {
34 #[inline]
35 fn clone(&self) -> Self {
36 *self
37 }
38}
39impl<T> Copy for Index<T> {}
40
41impl<T> fmt::Debug for Index<T> {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 match self.ndx {
44 u32::MAX => write!(f, "Index(-)"),
45 _ => write!(f, "{:?}", self.ndx),
46 }
47 }
48}
49
50impl<T> Default for Index<T> {
51 fn default() -> Self {
53 Self::NONE
54 }
55}
56
57impl<T> PartialEq for Index<T> {
58 #[inline]
59 fn eq(&self, other: &Self) -> bool {
60 self.ndx == other.ndx
61 }
62}
63
64impl<T> Hash for Index<T> {
65 #[inline]
66 fn hash<H: Hasher>(&self, state: &mut H) {
67 self.ndx.hash(state);
68 }
69}
70
71impl<T> Index<T> {
72 pub const NONE: Self = Index {
78 ndx: u32::MAX,
79 _marker: PhantomData,
80 };
81
82 #[inline]
94 pub fn is_some(&self) -> bool {
95 self.ndx != u32::MAX
96 }
97
98 #[inline]
110 pub fn is_none(&self) -> bool {
111 self.ndx == u32::MAX
112 }
113
114 #[inline]
119 pub(crate) fn get(&self) -> Option<usize> {
120 if self.is_some() {
121 Some(self.ndx as usize)
122 } else {
123 None
124 }
125 }
126}
127
128impl<T> From<u32> for Index<T> {
129 #[inline]
131 fn from(ndx: u32) -> Index<T> {
132 Self {
133 ndx,
134 _marker: PhantomData,
135 }
136 }
137}
138
139impl<T> From<usize> for Index<T> {
140 #[inline]
157 fn from(index: usize) -> Index<T> {
158 Index::from(u32::try_from(index).unwrap_or(u32::MAX))
159 }
160}
161
162impl<T> fmt::Display for Index<T> {
163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 match self.get() {
165 Some(n) => write!(f, "{}", n),
166 None => write!(f, "-"),
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use std::collections::HashSet;
175
176 #[derive(PartialEq, Eq)]
178 struct MyData;
179
180 #[test]
181 fn test_creation_and_constants() {
182 let index_from_u32 = Index::<MyData>::from(42_u32);
183 assert_eq!(index_from_u32.ndx, 42);
184
185 let index_from_usize = Index::<MyData>::from(123_usize);
186 assert_eq!(index_from_usize.ndx, 123);
187
188 assert_eq!(Index::<MyData>::NONE.ndx, u32::MAX);
190 }
191
192 #[test]
193 fn test_default() {
194 let default_index = Index::<MyData>::default();
196 assert_eq!(default_index, Index::NONE);
197 assert!(default_index.is_none());
198 }
199
200 #[test]
201 fn test_state_checks() {
202 let valid_index = Index::<MyData>::from(10_u32);
203 let none_index = Index::<MyData>::NONE;
204
205 assert!(valid_index.is_some());
207 assert!(!none_index.is_some());
208
209 assert!(!valid_index.is_none());
211 assert!(none_index.is_none());
212 }
213
214 #[test]
215 fn test_get() {
216 let valid_index = Index::<MyData>::from(99_u32);
217 let none_index = Index::<MyData>::NONE;
218
219 assert_eq!(valid_index.get(), Some(99_usize));
220 assert_eq!(none_index.get(), None);
221 }
222
223 #[test]
224 fn test_from_usize_overflow() {
225 let normal_usize = u32::MAX - 1;
227 let index1 = Index::<MyData>::from(normal_usize as usize);
228 assert_eq!(index1.ndx, u32::MAX - 1);
229 assert!(index1.is_some());
230
231 let overflow_usize_1 = u32::MAX;
233 let index2 = Index::<MyData>::from(overflow_usize_1 as usize);
234 assert_eq!(index2, Index::NONE);
235 assert!(index2.is_none());
236
237 let overflow_usize_2 = u64::MAX as usize;
239 let index3 = Index::<MyData>::from(overflow_usize_2);
240 assert_eq!(index3, Index::NONE);
241 assert!(index3.is_none());
242 }
243
244 #[test]
245 fn test_equality() {
246 let index1a = Index::<MyData>::from(1_u32);
247 let index1b = Index::<MyData>::from(1_u32);
248 let index2 = Index::<MyData>::from(2_u32);
249 let none1 = Index::<MyData>::NONE;
250 let none2 = Index::<MyData>::NONE;
251
252 assert_eq!(index1a, index1b);
253 assert_eq!(none1, none2);
254 assert_ne!(index1a, index2);
255 assert_ne!(index1a, none1);
256 }
257
258 #[test]
259 fn test_clone_and_copy() {
260 let original = Index::<MyData>::from(50_u32);
261
262 let copied = original;
264 assert_eq!(original, copied);
265
266 let cloned = original.clone();
268 assert_eq!(original, cloned);
269
270 assert_eq!(original.ndx, copied.ndx);
272 assert_eq!(original.ndx, cloned.ndx);
273 }
274
275 #[test]
276 fn test_hash() {
277 let mut set = HashSet::new();
278 let index1 = Index::<MyData>::from(1_u32);
279 let index1_dup = Index::<MyData>::from(1_u32);
280 let index2 = Index::<MyData>::from(2_u32);
281 let none_index = Index::<MyData>::NONE;
282
283 assert!(set.insert(index1));
284 assert!(set.insert(index2));
285 assert!(set.insert(none_index));
286
287 assert!(!set.insert(index1_dup));
289
290 assert_eq!(set.len(), 3);
292 assert!(set.contains(&index1));
293 assert!(set.contains(&index2));
294 assert!(set.contains(&none_index));
295 assert!(!set.contains(&Index::from(99_u32)));
296 }
297
298 #[test]
299 fn test_formatting() {
300 let valid_index = Index::<MyData>::from(123_u32);
301 let none_index = Index::<MyData>::NONE;
302
303 let debug_valid = format!("{:?}", valid_index);
305 let debug_none = format!("{:?}", none_index);
306 assert_eq!(debug_valid, "123");
307 assert_eq!(debug_none, "Index(-)");
308
309 let display_valid = format!("{}", valid_index);
311 let display_none = format!("{}", none_index);
312 assert_eq!(display_valid, "123");
313 assert_eq!(display_none, "-");
314 }
315}