versionize/
version_map.rs1use std::any::TypeId;
63use std::collections::hash_map::HashMap;
64use std::fmt::Debug;
65use std::sync::Arc;
66
67const BASE_VERSION: u16 = 1;
68
69pub trait VersionFilter: Debug {
71 fn is_supported(&self, version: u16) -> bool;
73}
74
75impl VersionFilter for () {
76 fn is_supported(&self, _version: u16) -> bool {
77 true
78 }
79}
80#[derive(Clone, Debug)]
84pub struct VersionMap {
85 versions: Vec<HashMap<TypeId, u16>>,
86 filter: Arc<dyn VersionFilter + Send + Sync>,
87}
88
89impl Default for VersionMap {
90 fn default() -> Self {
91 VersionMap {
92 versions: vec![HashMap::new()],
93 filter: Arc::new(()),
94 }
95 }
96}
97
98impl VersionMap {
99 pub fn new() -> Self {
101 Default::default()
102 }
103
104 pub fn with_filter(filter: Arc<dyn VersionFilter + Send + Sync>) -> Self {
106 VersionMap {
107 versions: vec![HashMap::new()],
108 filter,
109 }
110 }
111
112 pub fn new_version(&mut self) -> &mut Self {
114 self.versions.push(HashMap::new());
115 self
116 }
117
118 pub fn set_type_version(&mut self, type_id: TypeId, type_version: u16) -> &mut Self {
120 self.versions
122 .last_mut()
123 .unwrap()
124 .insert(type_id, type_version);
125 self
126 }
127
128 pub fn get_type_version(&self, root_version: u16, type_id: TypeId) -> u16 {
131 let version_space = if root_version > self.latest_version() || root_version == 0 {
132 self.versions.as_slice()
133 } else {
134 self.versions.split_at(root_version as usize).0
135 };
136
137 for i in (0..version_space.len()).rev() {
138 if let Some(version) = version_space[i].get(&type_id) {
139 return *version;
140 }
141 }
142
143 BASE_VERSION
144 }
145
146 pub fn latest_version(&self) -> u16 {
148 self.versions.len() as u16
149 }
150
151 pub fn is_supported(&self, version: u16) -> bool {
153 if version == 0 || version > self.latest_version() {
154 false
155 } else {
156 self.filter.is_supported(version)
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::{TypeId, VersionMap, BASE_VERSION};
164 use std::sync::Arc;
165 use version_map::VersionFilter;
166
167 pub struct MyType;
168 pub struct MySecondType;
169 pub struct MyThirdType;
170
171 #[derive(Debug)]
172 struct MyFilter;
173
174 impl VersionFilter for MyFilter {
175 fn is_supported(&self, version: u16) -> bool {
176 version < 5
177 }
178 }
179
180 #[test]
181 fn test_default_version() {
182 let vm = VersionMap::new();
183 assert_eq!(vm.latest_version(), 1);
184 }
185
186 #[test]
187 fn test_new_versions() {
188 let mut vm = VersionMap::new();
189 vm.new_version().new_version();
190 assert_eq!(vm.latest_version(), 3);
191 }
192
193 #[test]
194 fn test_1_app_version() {
195 let mut vm = VersionMap::new();
196 vm.set_type_version(TypeId::of::<MyType>(), 1);
197 vm.set_type_version(TypeId::of::<MySecondType>(), 2);
198 vm.set_type_version(TypeId::of::<MyThirdType>(), 3);
199
200 assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), 1);
201 assert_eq!(vm.get_type_version(1, TypeId::of::<MySecondType>()), 2);
202 assert_eq!(vm.get_type_version(1, TypeId::of::<MyThirdType>()), 3);
203 }
204
205 #[test]
206 fn test_100_app_version_full() {
207 let mut vm = VersionMap::new();
208
209 for i in 1..=100 {
210 vm.set_type_version(TypeId::of::<MyType>(), i)
211 .set_type_version(TypeId::of::<MySecondType>(), i + 1)
212 .set_type_version(TypeId::of::<MyThirdType>(), i + 2)
213 .new_version();
214 }
215
216 for i in 1..=100 {
217 assert_eq!(vm.get_type_version(i, TypeId::of::<MyType>()), i);
218 assert_eq!(vm.get_type_version(i, TypeId::of::<MySecondType>()), i + 1);
219 assert_eq!(vm.get_type_version(i, TypeId::of::<MyThirdType>()), i + 2);
220 }
221 }
222
223 #[test]
224 fn test_version_map_is_send_and_sync() {
225 fn assert_send_sync<T: Send + Sync>() {}
226
227 assert_send_sync::<VersionMap>();
228 }
229
230 #[test]
231 fn test_app_versions_with_gap() {
232 let my_type_id = TypeId::of::<MyType>();
233 let my_second_type_id = TypeId::of::<MySecondType>();
234 let my_third_type_id = TypeId::of::<MyThirdType>();
235
236 let mut vm = VersionMap::new();
237 vm.set_type_version(my_type_id, 1);
238 vm.set_type_version(my_second_type_id, 1);
239 vm.set_type_version(my_third_type_id, 1);
240 vm.new_version();
241 vm.set_type_version(my_type_id, 2);
242 vm.new_version();
243 vm.set_type_version(my_third_type_id, 2);
244 vm.new_version();
245 vm.set_type_version(my_second_type_id, 2);
246
247 assert_eq!(vm.get_type_version(1, my_type_id), 1);
248 assert_eq!(vm.get_type_version(1, my_second_type_id), 1);
249 assert_eq!(vm.get_type_version(1, my_third_type_id), 1);
250
251 assert_eq!(vm.get_type_version(2, my_type_id), 2);
252 assert_eq!(vm.get_type_version(2, my_second_type_id), 1);
253 assert_eq!(vm.get_type_version(2, my_third_type_id), 1);
254
255 assert_eq!(vm.get_type_version(3, my_type_id), 2);
256 assert_eq!(vm.get_type_version(3, my_second_type_id), 1);
257 assert_eq!(vm.get_type_version(3, my_third_type_id), 2);
258
259 assert_eq!(vm.get_type_version(4, my_type_id), 2);
260 assert_eq!(vm.get_type_version(4, my_second_type_id), 2);
261 assert_eq!(vm.get_type_version(4, my_third_type_id), 2);
262 }
263
264 #[test]
265 fn test_unset_type() {
266 let vm = VersionMap::new();
267 assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), BASE_VERSION);
268 }
269
270 #[test]
271 fn test_invalid_root_version() {
272 let mut vm = VersionMap::new();
273 vm.new_version().set_type_version(TypeId::of::<MyType>(), 2);
274
275 assert_eq!(vm.get_type_version(0, TypeId::of::<MyType>()), 2);
276
277 assert_eq!(vm.latest_version(), 2);
278 assert_eq!(vm.get_type_version(129, TypeId::of::<MyType>()), 2);
279 assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), BASE_VERSION);
280 }
281
282 #[test]
283 fn test_version_filter() {
284 let mut vm = VersionMap::default();
285 vm.new_version();
286
287 assert!(!vm.is_supported(0));
288 assert!(vm.is_supported(1));
289 assert!(vm.is_supported(2));
290 assert!(!vm.is_supported(3));
291
292 let mut vm = VersionMap::with_filter(Arc::new(MyFilter));
293 vm.new_version();
294 vm.new_version();
295 vm.new_version();
296 vm.new_version();
297 vm.new_version();
298
299 let vm1 = vm.clone();
300 assert!(!vm1.is_supported(0));
301 assert!(vm1.is_supported(1));
302 assert!(vm1.is_supported(2));
303 assert!(vm1.is_supported(3));
304 assert!(vm1.is_supported(4));
305 assert!(!vm1.is_supported(5));
306 assert!(!vm1.is_supported(6));
307 assert_eq!(vm.latest_version(), 6);
308 }
309}