rustlite_snapshot/
manager.rs1use crate::{SnapshotMeta, SnapshotType};
6use std::path::Path;
7
8pub struct SnapshotManagerImpl {
10 base_path: std::path::PathBuf,
12}
13
14impl SnapshotManagerImpl {
15 pub fn new(base_path: impl AsRef<Path>) -> Self {
17 Self {
18 base_path: base_path.as_ref().to_path_buf(),
19 }
20 }
21
22 pub fn should_be_incremental(&self, parent: Option<&SnapshotMeta>) -> bool {
24 parent.is_some()
25 }
26
27 pub fn calculate_diff<'a>(
29 &self,
30 old: &'a SnapshotMeta,
31 new_files: &'a [crate::SnapshotFile],
32 ) -> Vec<&'a crate::SnapshotFile> {
33 new_files
35 .iter()
36 .filter(|new_file| {
37 old.files
39 .iter()
40 .find(|old_file| old_file.relative_path == new_file.relative_path)
41 .map(|old_file| old_file.checksum != new_file.checksum)
42 .unwrap_or(true) })
44 .collect()
45 }
46
47 pub fn base_path(&self) -> &Path {
49 &self.base_path
50 }
51}
52
53pub struct SnapshotChain {
55 snapshots: Vec<SnapshotMeta>,
57}
58
59impl SnapshotChain {
60 pub fn new() -> Self {
62 Self {
63 snapshots: Vec::new(),
64 }
65 }
66
67 pub fn add(&mut self, snapshot: SnapshotMeta) {
69 self.snapshots.push(snapshot);
70 }
71
72 pub fn latest(&self) -> Option<&SnapshotMeta> {
74 self.snapshots.last()
75 }
76
77 pub fn chain(&self) -> &[SnapshotMeta] {
79 &self.snapshots
80 }
81
82 pub fn is_valid(&self) -> bool {
84 if self.snapshots.is_empty() {
85 return true;
86 }
87
88 if self.snapshots[0].snapshot_type != SnapshotType::Full {
90 return false;
91 }
92
93 for i in 1..self.snapshots.len() {
95 if self.snapshots[i].snapshot_type == SnapshotType::Incremental
96 && self.snapshots[i].parent_id.as_ref() != Some(&self.snapshots[i - 1].id)
97 {
98 return false;
99 }
100 }
101
102 true
103 }
104}
105
106impl Default for SnapshotChain {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_snapshot_chain_empty() {
118 let chain = SnapshotChain::new();
119 assert!(chain.is_valid());
120 assert!(chain.latest().is_none());
121 }
122
123 #[test]
124 fn test_snapshot_chain_with_full() {
125 let mut chain = SnapshotChain::new();
126 chain.add(SnapshotMeta {
127 id: "snap_1".to_string(),
128 timestamp: 1000,
129 path: "/backup/snap_1".to_string(),
130 source_path: "/db".to_string(),
131 sequence: 100,
132 files: vec![],
133 total_size: 0,
134 snapshot_type: SnapshotType::Full,
135 parent_id: None,
136 });
137
138 assert!(chain.is_valid());
139 assert_eq!(chain.latest().unwrap().id, "snap_1");
140 }
141}