use std::any::TypeId;
use std::collections::hash_map::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
const BASE_VERSION: u16 = 1;
pub trait VersionFilter: Debug {
fn is_supported(&self, version: u16) -> bool;
}
impl VersionFilter for () {
fn is_supported(&self, _version: u16) -> bool {
true
}
}
#[derive(Clone, Debug)]
pub struct VersionMap {
versions: Vec<HashMap<TypeId, u16>>,
filter: Arc<dyn VersionFilter + Send + Sync>,
}
impl Default for VersionMap {
fn default() -> Self {
VersionMap {
versions: vec![HashMap::new(); 1],
filter: Arc::new(()),
}
}
}
impl VersionMap {
pub fn new() -> Self {
Default::default()
}
pub fn with_filter(filter: Arc<dyn VersionFilter + Send + Sync>) -> Self {
VersionMap {
versions: vec![HashMap::new(); 1],
filter,
}
}
pub fn new_version(&mut self) -> &mut Self {
self.versions.push(HashMap::new());
self
}
pub fn set_type_version(&mut self, type_id: TypeId, type_version: u16) -> &mut Self {
self.versions
.last_mut()
.unwrap()
.insert(type_id, type_version);
self
}
pub fn get_type_version(&self, root_version: u16, type_id: TypeId) -> u16 {
let version_space = if root_version > self.latest_version() || root_version == 0 {
self.versions.as_slice()
} else {
self.versions.split_at(root_version as usize).0
};
for i in (0..version_space.len()).rev() {
if let Some(version) = version_space[i].get(&type_id) {
return *version;
}
}
BASE_VERSION
}
pub fn latest_version(&self) -> u16 {
self.versions.len() as u16
}
pub fn is_supported(&self, version: u16) -> bool {
if version == 0 || version > self.latest_version() {
false
} else {
self.filter.is_supported(version)
}
}
}
#[cfg(test)]
mod tests {
use super::{TypeId, VersionMap, BASE_VERSION};
use std::sync::Arc;
use version_map::VersionFilter;
pub struct MyType;
pub struct MySecondType;
pub struct MyThirdType;
#[derive(Debug)]
struct MyFilter;
impl VersionFilter for MyFilter {
fn is_supported(&self, version: u16) -> bool {
version < 5
}
}
#[test]
fn test_default_version() {
let vm = VersionMap::new();
assert_eq!(vm.latest_version(), 1);
}
#[test]
fn test_new_versions() {
let mut vm = VersionMap::new();
vm.new_version().new_version();
assert_eq!(vm.latest_version(), 3);
}
#[test]
fn test_1_app_version() {
let mut vm = VersionMap::new();
vm.set_type_version(TypeId::of::<MyType>(), 1);
vm.set_type_version(TypeId::of::<MySecondType>(), 2);
vm.set_type_version(TypeId::of::<MyThirdType>(), 3);
assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), 1);
assert_eq!(vm.get_type_version(1, TypeId::of::<MySecondType>()), 2);
assert_eq!(vm.get_type_version(1, TypeId::of::<MyThirdType>()), 3);
}
#[test]
fn test_100_app_version_full() {
let mut vm = VersionMap::new();
for i in 1..=100 {
vm.set_type_version(TypeId::of::<MyType>(), i)
.set_type_version(TypeId::of::<MySecondType>(), i + 1)
.set_type_version(TypeId::of::<MyThirdType>(), i + 2)
.new_version();
}
for i in 1..=100 {
assert_eq!(vm.get_type_version(i, TypeId::of::<MyType>()), i);
assert_eq!(vm.get_type_version(i, TypeId::of::<MySecondType>()), i + 1);
assert_eq!(vm.get_type_version(i, TypeId::of::<MyThirdType>()), i + 2);
}
}
#[test]
fn test_version_map_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<VersionMap>();
}
#[test]
fn test_app_versions_with_gap() {
let my_type_id = TypeId::of::<MyType>();
let my_second_type_id = TypeId::of::<MySecondType>();
let my_third_type_id = TypeId::of::<MyThirdType>();
let mut vm = VersionMap::new();
vm.set_type_version(my_type_id, 1);
vm.set_type_version(my_second_type_id, 1);
vm.set_type_version(my_third_type_id, 1);
vm.new_version();
vm.set_type_version(my_type_id, 2);
vm.new_version();
vm.set_type_version(my_third_type_id, 2);
vm.new_version();
vm.set_type_version(my_second_type_id, 2);
assert_eq!(vm.get_type_version(1, my_type_id), 1);
assert_eq!(vm.get_type_version(1, my_second_type_id), 1);
assert_eq!(vm.get_type_version(1, my_third_type_id), 1);
assert_eq!(vm.get_type_version(2, my_type_id), 2);
assert_eq!(vm.get_type_version(2, my_second_type_id), 1);
assert_eq!(vm.get_type_version(2, my_third_type_id), 1);
assert_eq!(vm.get_type_version(3, my_type_id), 2);
assert_eq!(vm.get_type_version(3, my_second_type_id), 1);
assert_eq!(vm.get_type_version(3, my_third_type_id), 2);
assert_eq!(vm.get_type_version(4, my_type_id), 2);
assert_eq!(vm.get_type_version(4, my_second_type_id), 2);
assert_eq!(vm.get_type_version(4, my_third_type_id), 2);
}
#[test]
fn test_unset_type() {
let vm = VersionMap::new();
assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), BASE_VERSION);
}
#[test]
fn test_invalid_root_version() {
let mut vm = VersionMap::new();
vm.new_version().set_type_version(TypeId::of::<MyType>(), 2);
assert_eq!(vm.get_type_version(0, TypeId::of::<MyType>()), 2);
assert_eq!(vm.latest_version(), 2);
assert_eq!(vm.get_type_version(129, TypeId::of::<MyType>()), 2);
assert_eq!(vm.get_type_version(1, TypeId::of::<MyType>()), BASE_VERSION);
}
#[test]
fn test_version_filter() {
let mut vm = VersionMap::default();
vm.new_version();
assert!(!vm.is_supported(0));
assert!(vm.is_supported(1));
assert!(vm.is_supported(2));
assert!(!vm.is_supported(3));
let mut vm = VersionMap::with_filter(Arc::new(MyFilter));
vm.new_version();
vm.new_version();
vm.new_version();
vm.new_version();
vm.new_version();
let vm1 = vm.clone();
assert!(!vm1.is_supported(0));
assert!(vm1.is_supported(1));
assert!(vm1.is_supported(2));
assert!(vm1.is_supported(3));
assert!(vm1.is_supported(4));
assert!(!vm1.is_supported(5));
assert!(!vm1.is_supported(6));
assert_eq!(vm.latest_version(), 6);
}
}