versionize/
version_map.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! A helper to map struct and enum versions to a sequence of root versions.
5//! This helper is required to support the versioning of a hierarchy of
6//! structures composed of individually versioned structures or enums.
7//!
8//! ```rust
9//! extern crate versionize;
10//! extern crate versionize_derive;
11//!
12//! use versionize::{VersionMap, Versionize, VersionizeResult};
13//! use versionize_derive::Versionize;
14//!
15//! #[derive(Versionize)]
16//! pub struct Struct1 {
17//!     a: u32,
18//!     #[version(start = 2)]
19//!     b: u8,
20//! }
21//!
22//! #[derive(Versionize)]
23//! pub struct Struct2 {
24//!     x: u32,
25//!     #[version(start = 2)]
26//!     y: u8,
27//! }
28//!
29//! #[derive(Versionize)]
30//! pub struct State {
31//!     struct1: Struct1,
32//!     struct2: Struct2,
33//! }
34//!
35//! let mut version_map = VersionMap::new(); //
36//! version_map
37//!     .new_version()
38//!     .set_type_version(Struct1::type_id(), 2)
39//!     .new_version()
40//!     .set_type_version(Struct2::type_id(), 2);
41//!
42//! // Check that there are 3 root versions.
43//! assert_eq!(version_map.latest_version(), 3);
44//!
45//! // Check that root version 1 has all structs at version 1.
46//! assert_eq!(version_map.get_type_version(1, Struct1::type_id()), 1);
47//! assert_eq!(version_map.get_type_version(1, Struct2::type_id()), 1);
48//! assert_eq!(version_map.get_type_version(1, State::type_id()), 1);
49//!
50//! // Check that root version 2 has Struct1 at version 2 and Struct2
51//! // at version 1.
52//! assert_eq!(version_map.get_type_version(2, Struct1::type_id()), 2);
53//! assert_eq!(version_map.get_type_version(2, Struct2::type_id()), 1);
54//! assert_eq!(version_map.get_type_version(2, State::type_id()), 1);
55//!
56//! // Check that root version 3 has Struct1 and Struct2 at version 2.
57//! assert_eq!(version_map.get_type_version(3, Struct1::type_id()), 2);
58//! assert_eq!(version_map.get_type_version(3, Struct2::type_id()), 2);
59//! assert_eq!(version_map.get_type_version(3, State::type_id()), 1);
60//! ```
61
62use std::any::TypeId;
63use std::collections::hash_map::HashMap;
64use std::fmt::Debug;
65use std::sync::Arc;
66
67const BASE_VERSION: u16 = 1;
68
69/// Trait to check whether is specific `version` is supported by a `VersionMap`.
70pub trait VersionFilter: Debug {
71    /// Check whether the `version` is supported or not.
72    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///
81/// The VersionMap API provides functionality to define the version for each
82/// type and attach them to specific root versions.
83#[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    /// Create a new version map initialized at version 1.
100    pub fn new() -> Self {
101        Default::default()
102    }
103
104    /// Create a new version map with specified version filter.
105    pub fn with_filter(filter: Arc<dyn VersionFilter + Send + Sync>) -> Self {
106        VersionMap {
107            versions: vec![HashMap::new()],
108            filter,
109        }
110    }
111
112    /// Bumps root version by 1 to create a new root version.
113    pub fn new_version(&mut self) -> &mut Self {
114        self.versions.push(HashMap::new());
115        self
116    }
117
118    /// Define a mapping between a specific type version and the latest root version.
119    pub fn set_type_version(&mut self, type_id: TypeId, type_version: u16) -> &mut Self {
120        // It is safe to unwrap since `self.versions` always has at least 1 element.
121        self.versions
122            .last_mut()
123            .unwrap()
124            .insert(type_id, type_version);
125        self
126    }
127
128    /// Returns the version of `type_id` corresponding to the specified `root_version`.
129    /// If `root_version` is out of range returns the version of `type_id` at latest version.
130    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    /// Returns the latest version.
147    pub fn latest_version(&self) -> u16 {
148        self.versions.len() as u16
149    }
150
151    /// Check whether the `version` is supported by the version map.
152    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}