vecdb/
version.rs

1use std::{
2    fs,
3    io::{self, Read},
4    iter::Sum,
5    ops::Add,
6    path::Path,
7};
8
9use allocative::Allocative;
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
11
12use crate::{Error, Result};
13
14/// Version tracking for data schema and computed values.
15///
16/// Used to detect when stored data needs to be recomputed due to changes
17/// in computation logic or source data versions. Supports validation
18/// against persisted versions to ensure compatibility.
19#[derive(
20    Allocative,
21    Default,
22    Debug,
23    Clone,
24    Copy,
25    PartialEq,
26    Eq,
27    PartialOrd,
28    Ord,
29    FromBytes,
30    IntoBytes,
31    Immutable,
32    KnownLayout,
33)]
34pub struct Version(u64);
35
36impl Version {
37    pub const ZERO: Self = Self(0);
38    pub const ONE: Self = Self(1);
39    pub const TWO: Self = Self(2);
40
41    pub const fn new(v: u64) -> Self {
42        Self(v)
43    }
44
45    pub fn write(&self, path: &Path) -> Result<(), io::Error> {
46        fs::write(path, self.as_bytes())
47    }
48
49    pub fn swap_bytes(self) -> Self {
50        Self(self.0.swap_bytes())
51    }
52
53    ///
54    /// Ok(true) if existed and is same.
55    ///
56    /// Ok(false) if didn't exist.
57    ///
58    pub fn validate(&self, path: &Path) -> Result<bool> {
59        if let Ok(prev_version) = Version::try_from(path) {
60            if prev_version != *self {
61                if prev_version.swap_bytes() == *self {
62                    return Err(Error::WrongEndian);
63                }
64                return Err(Error::DifferentVersion {
65                    found: prev_version,
66                    expected: *self,
67                });
68            }
69
70            Ok(true)
71        } else {
72            Ok(false)
73        }
74    }
75}
76
77impl From<Version> for u64 {
78    fn from(value: Version) -> u64 {
79        value.0
80    }
81}
82
83impl From<u64> for Version {
84    fn from(value: u64) -> Self {
85        Self(value)
86    }
87}
88
89impl TryFrom<&Path> for Version {
90    type Error = Error;
91    fn try_from(value: &Path) -> Result<Self, Self::Error> {
92        let mut buf = [0; 8];
93        fs::read(value)?.as_slice().read_exact(&mut buf)?;
94        Ok(*(Self::ref_from_bytes(&buf)?))
95    }
96}
97
98impl Add<Version> for Version {
99    type Output = Self;
100    fn add(self, rhs: Version) -> Self::Output {
101        Self(self.0 + rhs.0)
102    }
103}
104
105impl Sum for Version {
106    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
107        iter.fold(Self::ZERO, Add::add)
108    }
109}