1use std::{any::type_name, collections::HashMap, fmt::Debug};
2
3pub struct HashMapId<T> {
5 id_seed: u64,
6 store: HashMap<u64, T>,
7}
8
9impl<T> HashMapId<T>
10where
11 T: Send + Sync,
12{
13 pub fn new() -> Self {
14 Self {
15 id_seed: 0,
16 store: HashMap::new(),
17 }
18 }
19
20 pub fn add(&mut self, item: T) -> u64 {
21 let id = self.id_seed;
22 self.store.insert(id, item);
23 self.id_seed += 1;
24 id
25 }
26
27 pub fn remove(&mut self, id: u64) -> Option<T> {
28 self.store.remove(&id)
29 }
30
31 pub fn get_mut(&mut self, id: u64) -> Option<&mut T> {
32 self.store.get_mut(&id)
33 }
34
35 pub fn get(&self, id: u64) -> Option<&T> {
36 self.store.get(&id)
37 }
38}
39
40impl<T> Default for HashMapId<T>
41where
42 T: Send + Sync,
43{
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl<T> Debug for HashMapId<T> {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.debug_struct("HashMapId")
52 .field("id_seed", &self.id_seed)
53 .field("type", &type_name::<T>())
54 .finish()
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
65 fn get_returns_none_for_non_existent_item() {
66 let hash: HashMapId<i32> = HashMapId::new();
67 let item = hash.get(10);
68 assert!(item.is_none());
69 }
70
71 #[test]
72 fn get_returns_reference_to_item() {
73 let mut hash: HashMapId<i32> = HashMapId::new();
74 let value = 10;
75 let id = hash.add(value);
76 let item = hash.get(id);
77 assert_eq!(item, Some(&value));
78 }
79
80 #[test]
83 fn get_mut_returns_mutable_reference() {
84 let mut hash: HashMapId<i32> = HashMapId::new();
85 let value = 10;
86 let id = hash.add(value);
87 let item = hash.get_mut(id);
88 assert_eq!(item, Some(&mut value.clone()));
89 }
90
91 #[test]
92 fn get_mut_returns_none_for_non_existent_item() {
93 let mut hash: HashMapId<i32> = HashMapId::new();
94 let item = hash.get_mut(0);
95 assert!(item.is_none());
96 }
97
98 #[test]
101 fn add_adds_item_to_store() {
102 let mut hash: HashMapId<i32> = HashMapId::new();
103 let item = 10;
104 let id = hash.add(item);
105 assert_eq!(hash.get(id), Some(&item));
106 }
107
108 #[test]
109 fn add_can_add_multiple_items() {
110 let mut hash: HashMapId<i32> = HashMapId::new();
111 let item1 = 10;
112 let item2 = 20;
113 let id1 = hash.add(item1);
114 let id2 = hash.add(item2);
115 assert_ne!(id1, id2);
116 assert_eq!(hash.get(id1), Some(&item1));
117 assert_eq!(hash.get(id2), Some(&item2));
118 }
119
120 #[test]
121 fn add_increments_id() {
122 let mut hash: HashMapId<i32> = HashMapId::new();
123 let id1 = hash.add(10);
124 let id2 = hash.add(20);
125 assert_eq!(id2, id1 + 1);
126 }
127
128 #[test]
129 #[should_panic]
130 fn add_panics_on_integer_overflow() {
131 let mut hash: HashMapId<i32> = HashMapId::new();
132 let item = 10;
133 hash.id_seed = std::u64::MAX;
134 hash.add(item);
135 }
136
137 #[test]
140 fn remove_handles_non_existent_id() {
141 let mut hash: HashMapId<i32> = HashMapId::new();
142 let result = hash.remove(1);
143 assert!(result.is_none());
144 }
145
146 #[test]
147 fn remove_returns_removed_value() {
148 let mut hash: HashMapId<i32> = HashMapId::new();
149 let value = 10;
150 let id = hash.add(value);
151 let removed_value = hash.remove(id);
152 assert_eq!(removed_value, Some(value));
153 }
154
155 #[test]
156 fn remove_removes_id() {
157 let mut hash: HashMapId<i32> = HashMapId::new();
158 let id = hash.add(10);
159 hash.remove(id);
160 assert_eq!(hash.get(id), None);
161 }
162
163 #[test]
164 fn remove_does_not_affect_other_items() {
165 let mut hash: HashMapId<i32> = HashMapId::new();
166
167 let value1 = 10;
168 let value2 = 20;
169 let value3 = 30;
170
171 let id1 = hash.add(value1);
172 let id2 = hash.add(value2);
173 let id3 = hash.add(value3);
174
175 hash.remove(id2);
176
177 assert_eq!(hash.get(id1), Some(&value1));
178 assert!(hash.get(id2).is_none());
179 assert_eq!(hash.get(id3), Some(&value3));
180 }
181
182 #[test]
185 fn default_creates_new_hashmapid() {
186 let hash: HashMapId<i32> = HashMapId::default();
187 assert_eq!(hash.id_seed, 0);
188 assert!(hash.store.is_empty());
189 }
190
191 #[test]
194 fn fmt_formats_the_hashmapid() {
195 let mut hash: HashMapId<i32> = HashMapId::default();
196 hash.add(10);
197
198 let expected = format!("HashMapId {{ id_seed: {}, type: \"i32\" }}", hash.id_seed);
199 let result = format!("{:?}", hash);
200
201 assert_eq!(result, expected);
202 }
203}