1use std::fmt;
2use std::hash::{Hash, Hasher};
3use std::marker::PhantomData;
4
5pub struct Id<T> {
15 value: u64,
16 _marker: PhantomData<fn() -> T>,
17}
18
19impl<T> Id<T> {
20 pub const fn new(value: u64) -> Self {
21 Self {
22 value,
23 _marker: PhantomData,
24 }
25 }
26
27 pub const fn value(&self) -> u64 {
28 self.value
29 }
30}
31
32impl<T> Clone for Id<T> {
33 fn clone(&self) -> Self {
34 *self
35 }
36}
37
38impl<T> Copy for Id<T> {}
39
40impl<T> PartialEq for Id<T> {
41 fn eq(&self, other: &Self) -> bool {
42 self.value == other.value
43 }
44}
45
46impl<T> Eq for Id<T> {}
47
48impl<T> Hash for Id<T> {
49 fn hash<H: Hasher>(&self, state: &mut H) {
50 self.value.hash(state);
51 }
52}
53
54impl<T> fmt::Debug for Id<T> {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.debug_tuple("Id").field(&self.value).finish()
57 }
58}
59
60impl<T> fmt::Display for Id<T> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "{}", self.value)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 struct Route;
71 struct Interface;
72
73 #[test]
74 fn same_value_is_equal_within_the_same_domain() {
75 assert_eq!(Id::<Route>::new(1), Id::<Route>::new(1));
76 assert_ne!(Id::<Route>::new(1), Id::<Route>::new(2));
77 }
78
79 #[test]
80 fn different_domains_are_distinct_types() {
81 let route_id = Id::<Route>::new(1);
86 let interface_id = Id::<Interface>::new(1);
87 assert_eq!(route_id.value(), interface_id.value());
88 }
89
90 #[test]
91 fn value_round_trips() {
92 assert_eq!(Id::<Route>::new(42).value(), 42);
93 }
94
95 #[test]
96 fn id_clone_hash_and_format_use_the_underlying_value() {
97 use std::collections::hash_map::DefaultHasher;
98
99 let id = Id::<Route>::new(42);
100 assert_eq!(id, id.clone());
101 assert_eq!(id.to_string(), "42");
102 assert_eq!(format!("{id:?}"), "Id(42)");
103
104 let mut first = DefaultHasher::new();
105 id.hash(&mut first);
106 let mut second = DefaultHasher::new();
107 Id::<Route>::new(42).hash(&mut second);
108 assert_eq!(first.finish(), second.finish());
109 }
110}