1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Archive manifest versioning.
//!
//! Tracks the schema / format version of an archive manifest so that readers
//! can refuse to open incompatible archives and writers can bump the version
//! when the format changes.
//!
//! # Compatibility policy
//!
//! A version `v` is considered **compatible** with another version `other` when
//! `other <= v` (i.e., a newer reader can always open an older manifest, but an
//! older reader should refuse to open a newer manifest).
//!
//! # Example
//! ```rust
//! use oximedia_archive::versioning::ArchiveManifestVersion;
//!
//! let mut ver = ArchiveManifestVersion::new(1);
//! assert!(ver.is_compatible(1));
//! ver.bump();
//! assert_eq!(ver.version(), 2);
//! assert!(!ver.is_compatible(3)); // cannot open a future version
//! ```
#![allow(dead_code)]
/// Version number attached to an archive manifest.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ArchiveManifestVersion {
version: u32,
}
impl ArchiveManifestVersion {
/// Create a new manifest version with the given version number.
///
/// `v = 0` is reserved for "unversioned / legacy"; the minimum meaningful
/// version is `1`.
#[must_use]
pub const fn new(v: u32) -> Self {
Self { version: v }
}
/// Return the current version number.
#[must_use]
pub const fn version(self) -> u32 {
self.version
}
/// Increment the version number by 1.
pub fn bump(&mut self) {
self.version = self.version.saturating_add(1);
}
/// Return `true` when `other` is compatible with this version.
///
/// An archive written at version `other` can be read by a reader at version
/// `self` if and only if `other <= self.version`. In other words, a reader
/// must be at least as new as the archive.
#[must_use]
pub fn is_compatible(self, other: u32) -> bool {
other <= self.version
}
/// Minimum version number this reader supports (constant `1`).
#[must_use]
pub const fn min_supported() -> u32 {
1
}
}
impl std::fmt::Display for ArchiveManifestVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "v{}", self.version)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_stores_version() {
let v = ArchiveManifestVersion::new(3);
assert_eq!(v.version(), 3);
}
#[test]
fn test_bump_increments() {
let mut v = ArchiveManifestVersion::new(1);
v.bump();
assert_eq!(v.version(), 2);
v.bump();
assert_eq!(v.version(), 3);
}
#[test]
fn test_bump_saturation_at_u32_max() {
let mut v = ArchiveManifestVersion::new(u32::MAX);
v.bump(); // Must not overflow
assert_eq!(v.version(), u32::MAX);
}
#[test]
fn test_is_compatible_same_version() {
let v = ArchiveManifestVersion::new(2);
assert!(v.is_compatible(2));
}
#[test]
fn test_is_compatible_older_archive() {
let v = ArchiveManifestVersion::new(5);
assert!(v.is_compatible(1));
assert!(v.is_compatible(4));
}
#[test]
fn test_is_compatible_newer_archive_rejected() {
let v = ArchiveManifestVersion::new(2);
assert!(!v.is_compatible(3));
assert!(!v.is_compatible(100));
}
#[test]
fn test_display() {
let v = ArchiveManifestVersion::new(7);
assert_eq!(v.to_string(), "v7");
}
#[test]
fn test_ordering() {
let v1 = ArchiveManifestVersion::new(1);
let v3 = ArchiveManifestVersion::new(3);
assert!(v1 < v3);
assert!(v3 > v1);
}
}