version_track/
version.rs

1use uuid::Uuid;
2
3/// Tracks the changing state of the rebase file
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Version {
6	id: Uuid,
7	counter: usize,
8}
9
10/// A sentinel value version, that is useful as an initial non-version for caching.
11pub const SENTINEL_VERSION: Version = Version::sentinel();
12
13impl Version {
14	/// Creates a sentinel [Self], that is useful as an initial non-version for caching. Any changes to this
15	/// version will reset the internal state to a new value. Internally, this uses a [Uuid] with the maximum valid
16	/// value, and an increment of [`usize::MAX`].
17	///
18	/// See [`SENTINEL_VERSION`] for a constant sentinel value.
19	#[inline]
20	#[must_use]
21	pub const fn sentinel() -> Self {
22		Self {
23			id: Uuid::from_bytes([0xFF; 16]),
24			counter: usize::MAX,
25		}
26	}
27
28	/// Create a new [Self] instance with a random [Uuid] and an increment of 0.
29	#[inline]
30	#[must_use]
31	pub fn new() -> Self {
32		Self {
33			id: Uuid::new_v4(),
34			counter: 0,
35		}
36	}
37
38	/// Reset to an initial state, with a new internal version tracker.
39	#[inline]
40	pub fn reset(&mut self) {
41		self.id = Uuid::new_v4();
42		self.counter = 0;
43	}
44
45	/// Increment the internal internal version, if the increment overflows, then the internal [Uuid] is recreated with
46	/// a new value, and the increment is set to 0.
47	///
48	/// See [`Self::increment_wrap`] for a version that does not update the internal [Uuid] on overflow.
49	#[inline]
50	pub fn increment(&mut self) {
51		self.counter = self.counter.wrapping_add(1);
52		if self.counter == 0 {
53			self.id = Uuid::new_v4();
54		}
55	}
56
57	/// Increment the internal internal version, if the increment overflows, the increment resets to 0. This allows the
58	/// internal [Uuid] to remain stable, and reusing older increments are okay.
59	///
60	/// See [`Self::increment`] for a version that refreshes the internal [Uuid] on overflow.
61	#[inline]
62	pub fn increment_wrap(&mut self) {
63		self.counter = self.counter.wrapping_add(1);
64	}
65
66	/// Check if the internal [Uuid] is the same for this version and another. This is useful for checking if the state
67	/// being treated by this instance is the same as another.
68	///
69	/// Note that using [`Self::reset`] or [`Self::increment`] can update the internal [Uuid], and result in this value
70	/// returning false.
71	#[inline]
72	#[must_use]
73	pub fn alike(&self, other: &Self) -> bool {
74		self.id == other.id
75	}
76}
77
78#[cfg(test)]
79mod tests {
80	use super::*;
81
82	#[test]
83	fn sentinel() {
84		let version = Version::sentinel();
85		assert_eq!(version.id.as_u128(), u128::MAX);
86		assert_eq!(version.counter, usize::MAX);
87	}
88
89	#[test]
90	fn new() {
91		let version = Version::new();
92		assert!(version.id.as_u128() > 0);
93		assert_eq!(version.counter, 0);
94	}
95
96	#[test]
97	fn reset() {
98		let mut version = Version::new();
99		version.counter = 42;
100		let prev_id = version.id;
101		version.reset();
102		assert_ne!(version.id, prev_id);
103		assert_eq!(version.counter, 0);
104	}
105
106	#[test]
107	fn increment() {
108		let mut version = Version::new();
109		let prev_id = version.id;
110		version.increment();
111		assert_eq!(version.id, prev_id);
112		assert_eq!(version.counter, 1);
113	}
114
115	#[test]
116	fn increment_with_wrap() {
117		let mut version = Version::new();
118		version.counter = usize::MAX;
119		let prev_id = version.id;
120		version.increment();
121		assert_ne!(version.id, prev_id);
122		assert_eq!(version.counter, 0);
123	}
124
125	#[test]
126	fn increment_wrap() {
127		let mut version = Version::new();
128		let prev_id = version.id;
129		version.increment_wrap();
130		assert_eq!(version.id, prev_id);
131		assert_eq!(version.counter, 1);
132	}
133
134	#[test]
135	fn increment_wrap_with_wrap() {
136		let mut version = Version::new();
137		version.counter = usize::MAX;
138		let prev_id = version.id;
139		version.increment_wrap();
140		assert_eq!(version.id, prev_id);
141		assert_eq!(version.counter, 0);
142	}
143
144	#[test]
145	fn alike_match() {
146		let version = Version::new();
147		let other = version;
148		assert!(other.alike(&version));
149	}
150
151	#[test]
152	fn alike_mismatch() {
153		let version = Version::new();
154		let other = Version::new();
155		assert!(!other.alike(&version));
156	}
157}