1use core::num::NonZeroU64;
10use serde::{Deserialize, Serialize};
11
12#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct InstanceId(NonZeroU64);
17
18impl InstanceId {
19 #[inline]
21 pub const fn new(v: u64) -> Option<Self> {
22 match NonZeroU64::new(v) {
23 Some(n) => Some(Self(n)),
24 None => None,
25 }
26 }
27
28 #[inline]
30 pub const fn get(self) -> u64 {
31 self.0.get()
32 }
33}
34
35#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
37#[serde(transparent)]
38pub struct EntityId(NonZeroU64);
39
40impl EntityId {
41 #[inline]
43 pub const fn new(v: u64) -> Option<Self> {
44 match NonZeroU64::new(v) {
45 Some(n) => Some(Self(n)),
46 None => None,
47 }
48 }
49
50 #[inline]
52 pub const fn get(self) -> u64 {
53 self.0.get()
54 }
55}
56
57#[derive(
60 Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Serialize, Deserialize,
61)]
62#[serde(transparent)]
63pub struct Tick(pub u64);
64
65impl Tick {
66 pub const ZERO: Tick = Tick(0);
68
69 #[inline]
71 pub const fn advance(self, delta: u64) -> Tick {
72 Tick(self.0.saturating_add(delta))
73 }
74}
75
76#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
80#[serde(transparent)]
81pub struct TypeCode(pub u32);
82
83#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
87#[serde(transparent)]
88pub struct RouteId(pub u32);
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn instance_id_zero_is_unrepresentable() {
96 assert!(InstanceId::new(0).is_none());
97 }
98
99 #[test]
100 fn instance_id_nonzero_roundtrip() {
101 let id = InstanceId::new(42).expect("42 is non-zero");
102 assert_eq!(id.get(), 42);
103 }
104
105 #[test]
106 fn entity_id_zero_is_unrepresentable() {
107 assert!(EntityId::new(0).is_none());
108 }
109
110 #[test]
111 fn entity_id_nonzero_roundtrip() {
112 let id = EntityId::new(u64::MAX).expect("max is non-zero");
113 assert_eq!(id.get(), u64::MAX);
114 }
115
116 #[test]
117 fn tick_advance_saturates_without_wrapping() {
118 assert_eq!(Tick::ZERO.advance(100).0, 100);
119 assert_eq!(Tick(u64::MAX).advance(1).0, u64::MAX);
120 assert_eq!(Tick(u64::MAX - 5).advance(10).0, u64::MAX);
121 }
122
123 #[test]
124 fn type_code_and_route_id_are_totally_ordered() {
125 assert!(TypeCode(1) < TypeCode(2));
126 assert!(RouteId(10) > RouteId(5));
127 }
128
129 #[test]
130 fn ids_are_copy_and_eq() {
131 fn assert_copy<T: Copy>() {}
133 fn assert_eq<T: Eq>() {}
134 fn assert_ord<T: Ord>() {}
135 fn assert_hash<T: core::hash::Hash>() {}
136 assert_copy::<InstanceId>();
137 assert_copy::<EntityId>();
138 assert_copy::<Tick>();
139 assert_copy::<TypeCode>();
140 assert_copy::<RouteId>();
141 assert_eq::<InstanceId>();
142 assert_ord::<InstanceId>();
143 assert_hash::<InstanceId>();
144 }
145}