1use serde::{Deserialize, Serialize};
15
16use crate::error::{AxCode, AxError};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20#[serde(transparent)]
21pub struct Version(u64);
22
23impl Version {
24 pub const FIRST: Version = Version(1);
26
27 pub fn new(value: u64) -> Self {
28 Version(value)
29 }
30
31 pub fn value(&self) -> u64 {
32 self.0
33 }
34
35 pub fn next(self) -> Result<Version, AxError> {
36 self.0.checked_add(1).map(Version).ok_or_else(|| {
37 AxError::failure(AxCode::InvalidArgs, "advance version", self.0.to_string())
38 .with_recovery("version space exhausted for this subject")
39 })
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum VersionVerdict {
46 Fresh,
47 Stale { current: Version },
48}
49
50pub fn check_base(current: Version, base: Version) -> VersionVerdict {
53 if base == current {
54 VersionVerdict::Fresh
55 } else {
56 VersionVerdict::Stale { current }
57 }
58}
59
60#[cfg(test)]
61#[allow(
62 clippy::unwrap_used,
63 clippy::expect_used,
64 clippy::panic,
65 clippy::indexing_slicing,
66 reason = "test code"
67)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn first_version_is_one_and_advances_checked() {
73 assert_eq!(Version::FIRST.value(), 1);
74 assert_eq!(Version::FIRST.next().unwrap().value(), 2);
75 assert!(Version::new(u64::MAX).next().is_err());
76 }
77
78 #[test]
79 fn matching_base_is_fresh() {
80 let current = Version::new(7);
81 assert_eq!(check_base(current, Version::new(7)), VersionVerdict::Fresh);
82 }
83
84 #[test]
85 fn stale_and_ahead_bases_both_report_stale_with_current() {
86 let current = Version::new(7);
87 for base in [Version::new(6), Version::new(8)] {
88 assert_eq!(
89 check_base(current, base),
90 VersionVerdict::Stale { current },
91 "only `current` is the truth; an ahead base is caller fiction"
92 );
93 }
94 }
95
96 #[test]
97 fn serde_is_transparent_u64() {
98 assert_eq!(serde_json::to_string(&Version::new(3)).unwrap(), "3");
99 let v: Version = serde_json::from_str("9").unwrap();
100 assert_eq!(v, Version::new(9));
101 }
102}