use super::version::Version;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompatibilityLevel {
Compatible,
CompatibleWithWarnings,
Incompatible,
}
impl CompatibilityLevel {
#[inline]
pub const fn is_usable(&self) -> bool {
!matches!(self, CompatibilityLevel::Incompatible)
}
#[inline]
pub const fn is_fully_compatible(&self) -> bool {
matches!(self, CompatibilityLevel::Compatible)
}
#[inline]
pub const fn has_warnings(&self) -> bool {
matches!(self, CompatibilityLevel::CompatibleWithWarnings)
}
}
pub fn check_compatibility(
data_version: Version,
current_version: Version,
min_compatible: Option<Version>,
) -> CompatibilityLevel {
if let Some(min) = min_compatible {
if data_version < min {
return CompatibilityLevel::Incompatible;
}
}
if current_version.major == 0 && data_version.major == 0 {
if data_version.minor != current_version.minor {
return CompatibilityLevel::Incompatible;
}
return CompatibilityLevel::Compatible;
}
if data_version.major != current_version.major {
return CompatibilityLevel::Incompatible;
}
if data_version > current_version {
return CompatibilityLevel::CompatibleWithWarnings;
}
if data_version.minor < current_version.minor {
return CompatibilityLevel::CompatibleWithWarnings;
}
CompatibilityLevel::Compatible
}
pub fn can_migrate(from: Version, to: Version) -> bool {
if from.major == to.major {
return true;
}
from < to
}
#[cfg(feature = "alloc")]
pub fn migration_path(from: Version, to: Version) -> alloc::vec::Vec<Version> {
let mut path = alloc::vec::Vec::new();
if from == to {
return path;
}
let mut current = from;
while current.major < to.major {
current = Version::new(current.major + 1, 0, 0);
if current != to {
path.push(current);
}
}
path
}
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compatible_same_version() {
let v = Version::new(1, 0, 0);
let compat = check_compatibility(v, v, None);
assert_eq!(compat, CompatibilityLevel::Compatible);
}
#[test]
fn test_compatible_patch_difference() {
let data = Version::new(1, 0, 0);
let current = Version::new(1, 0, 5);
let compat = check_compatibility(data, current, None);
assert_eq!(compat, CompatibilityLevel::Compatible);
}
#[test]
fn test_compatible_with_warnings_minor() {
let data = Version::new(1, 0, 0);
let current = Version::new(1, 5, 0);
let compat = check_compatibility(data, current, None);
assert_eq!(compat, CompatibilityLevel::CompatibleWithWarnings);
}
#[test]
fn test_compatible_with_warnings_newer() {
let data = Version::new(1, 5, 0);
let current = Version::new(1, 0, 0);
let compat = check_compatibility(data, current, None);
assert_eq!(compat, CompatibilityLevel::CompatibleWithWarnings);
}
#[test]
fn test_incompatible_major() {
let data = Version::new(1, 0, 0);
let current = Version::new(2, 0, 0);
let compat = check_compatibility(data, current, None);
assert_eq!(compat, CompatibilityLevel::Incompatible);
}
#[test]
fn test_incompatible_below_minimum() {
let data = Version::new(1, 0, 0);
let current = Version::new(1, 5, 0);
let min = Some(Version::new(1, 2, 0));
let compat = check_compatibility(data, current, min);
assert_eq!(compat, CompatibilityLevel::Incompatible);
}
#[test]
fn test_0x_compatibility() {
let data = Version::new(0, 1, 0);
let current = Version::new(0, 1, 5);
assert_eq!(
check_compatibility(data, current, None),
CompatibilityLevel::Compatible
);
let current2 = Version::new(0, 2, 0);
assert_eq!(
check_compatibility(data, current2, None),
CompatibilityLevel::Incompatible
);
}
#[test]
fn test_can_migrate() {
assert!(can_migrate(Version::new(1, 0, 0), Version::new(1, 5, 0)));
assert!(can_migrate(Version::new(1, 0, 0), Version::new(2, 0, 0)));
assert!(!can_migrate(Version::new(2, 0, 0), Version::new(1, 0, 0)));
}
#[cfg(feature = "alloc")]
#[test]
fn test_migration_path() {
let path = migration_path(Version::new(1, 0, 0), Version::new(1, 0, 0));
assert!(path.is_empty());
let path = migration_path(Version::new(1, 0, 0), Version::new(1, 5, 0));
assert!(path.is_empty());
let path = migration_path(Version::new(1, 0, 0), Version::new(2, 0, 0));
assert!(path.is_empty());
let path = migration_path(Version::new(1, 0, 0), Version::new(3, 0, 0));
assert_eq!(path.len(), 1);
assert_eq!(path[0], Version::new(2, 0, 0));
}
#[test]
fn test_compatibility_level_methods() {
assert!(CompatibilityLevel::Compatible.is_usable());
assert!(CompatibilityLevel::CompatibleWithWarnings.is_usable());
assert!(!CompatibilityLevel::Incompatible.is_usable());
assert!(CompatibilityLevel::Compatible.is_fully_compatible());
assert!(!CompatibilityLevel::CompatibleWithWarnings.is_fully_compatible());
assert!(!CompatibilityLevel::Compatible.has_warnings());
assert!(CompatibilityLevel::CompatibleWithWarnings.has_warnings());
}
}