driven/format/
versioning.rs1use crate::{DrivenError, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
7pub struct FormatVersion {
8 pub major: u8,
9 pub minor: u8,
10}
11
12impl FormatVersion {
13 pub const CURRENT: FormatVersion = FormatVersion { major: 1, minor: 0 };
15
16 pub const fn new(major: u8, minor: u8) -> Self {
18 Self { major, minor }
19 }
20
21 pub const fn to_u16(self) -> u16 {
23 (self.major as u16) << 8 | self.minor as u16
24 }
25
26 pub const fn from_u16(value: u16) -> Self {
28 Self {
29 major: (value >> 8) as u8,
30 minor: (value & 0xFF) as u8,
31 }
32 }
33
34 #[allow(clippy::absurd_extreme_comparisons)]
36 pub fn is_compatible(&self) -> bool {
37 self.major == Self::CURRENT.major && self.minor <= Self::CURRENT.minor
38 }
39}
40
41impl std::fmt::Display for FormatVersion {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}.{}", self.major, self.minor)
44 }
45}
46
47#[derive(Debug)]
49pub struct VersionMigrator;
50
51impl VersionMigrator {
52 pub fn migrate(data: &[u8], from: FormatVersion) -> Result<Vec<u8>> {
54 if from >= FormatVersion::CURRENT {
55 return Ok(data.to_vec());
57 }
58
59 if from.major != FormatVersion::CURRENT.major {
60 return Err(DrivenError::InvalidBinary(format!(
61 "Cannot migrate from version {} to {} (major version mismatch)",
62 from,
63 FormatVersion::CURRENT
64 )));
65 }
66
67 let migrated = data.to_vec();
69
70 Ok(migrated)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_version_roundtrip() {
86 let version = FormatVersion::new(1, 5);
87 let as_u16 = version.to_u16();
88 let back = FormatVersion::from_u16(as_u16);
89
90 assert_eq!(version, back);
91 }
92
93 #[test]
94 fn test_version_ordering() {
95 let v1_0 = FormatVersion::new(1, 0);
96 let v1_1 = FormatVersion::new(1, 1);
97 let v2_0 = FormatVersion::new(2, 0);
98
99 assert!(v1_0 < v1_1);
100 assert!(v1_1 < v2_0);
101 }
102
103 #[test]
104 fn test_compatibility() {
105 let current = FormatVersion::CURRENT;
106 assert!(current.is_compatible());
107
108 let older = FormatVersion::new(current.major, 0);
109 assert!(older.is_compatible());
110
111 let newer_minor = FormatVersion::new(current.major, current.minor + 1);
112 assert!(!newer_minor.is_compatible());
113
114 let newer_major = FormatVersion::new(current.major + 1, 0);
115 assert!(!newer_major.is_compatible());
116 }
117
118 #[test]
119 fn test_display() {
120 let version = FormatVersion::new(1, 5);
121 assert_eq!(format!("{}", version), "1.5");
122 }
123}