1use super::error::Error;
10use super::manifest::{
11 ManifestStore, ManifestStoreScan, NamedRootSelection, NamedRootUpdate, RootManifest,
12};
13use super::store::Store;
14use super::tree::Tree;
15use super::Prolly;
16
17pub const SNAPSHOT_BRANCH_PREFIX: &[u8] = b"refs/heads/";
19pub const SNAPSHOT_TAG_PREFIX: &[u8] = b"refs/tags/";
21pub const SNAPSHOT_CHECKPOINT_PREFIX: &[u8] = b"refs/checkpoints/";
23
24#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum SnapshotNamespace {
27 Branch,
29 Tag,
31 Checkpoint,
33 Custom(Vec<u8>),
35}
36
37impl SnapshotNamespace {
38 pub fn branch() -> Self {
40 Self::Branch
41 }
42
43 pub fn tag() -> Self {
45 Self::Tag
46 }
47
48 pub fn checkpoint() -> Self {
50 Self::Checkpoint
51 }
52
53 pub fn custom(prefix: impl Into<Vec<u8>>) -> Self {
55 Self::Custom(prefix.into())
56 }
57
58 pub fn prefix(&self) -> &[u8] {
60 match self {
61 Self::Branch => SNAPSHOT_BRANCH_PREFIX,
62 Self::Tag => SNAPSHOT_TAG_PREFIX,
63 Self::Checkpoint => SNAPSHOT_CHECKPOINT_PREFIX,
64 Self::Custom(prefix) => prefix,
65 }
66 }
67
68 pub fn root_name(&self, id: impl AsRef<[u8]>) -> Vec<u8> {
70 snapshot_root_name(self, id)
71 }
72
73 pub fn id_from_name(&self, name: impl AsRef<[u8]>) -> Option<Vec<u8>> {
75 snapshot_id_from_name(self, name)
76 }
77}
78
79pub fn snapshot_root_name(namespace: &SnapshotNamespace, id: impl AsRef<[u8]>) -> Vec<u8> {
81 let prefix = namespace.prefix();
82 let id = id.as_ref();
83 let mut name = Vec::with_capacity(prefix.len() + id.len());
84 name.extend_from_slice(prefix);
85 name.extend_from_slice(id);
86 name
87}
88
89pub fn snapshot_id_from_name(
91 namespace: &SnapshotNamespace,
92 name: impl AsRef<[u8]>,
93) -> Option<Vec<u8>> {
94 let prefix = namespace.prefix();
95 let name = name.as_ref();
96 name.strip_prefix(prefix).map(<[u8]>::to_vec)
97}
98
99#[derive(Clone, Debug, PartialEq)]
101pub struct SnapshotRoot {
102 pub id: Vec<u8>,
104 pub name: Vec<u8>,
106 pub tree: Tree,
108 pub created_at_millis: Option<u64>,
110 pub updated_at_millis: Option<u64>,
112}
113
114impl SnapshotRoot {
115 fn from_manifest(
116 namespace: &SnapshotNamespace,
117 name: Vec<u8>,
118 manifest: RootManifest,
119 ) -> Option<Self> {
120 let id = snapshot_id_from_name(namespace, &name)?;
121 let created_at_millis = manifest.created_at_millis;
122 let updated_at_millis = manifest.updated_at_millis;
123 Some(Self {
124 id,
125 name,
126 tree: manifest.into_tree(),
127 created_at_millis,
128 updated_at_millis,
129 })
130 }
131}
132
133#[derive(Clone, Debug, Default, PartialEq)]
135pub struct SnapshotSelection {
136 pub snapshots: Vec<SnapshotRoot>,
138 pub missing_ids: Vec<Vec<u8>>,
140}
141
142pub struct SnapshotManager<'a, S: Store> {
144 prolly: &'a Prolly<S>,
145 namespace: SnapshotNamespace,
146}
147
148impl<'a, S: Store> SnapshotManager<'a, S> {
149 pub fn new(prolly: &'a Prolly<S>, namespace: SnapshotNamespace) -> Self {
151 Self { prolly, namespace }
152 }
153
154 pub fn namespace(&self) -> &SnapshotNamespace {
156 &self.namespace
157 }
158
159 pub fn root_name(&self, id: impl AsRef<[u8]>) -> Vec<u8> {
161 self.namespace.root_name(id)
162 }
163
164 pub fn load(&self, id: impl AsRef<[u8]>) -> Result<Option<Tree>, Error>
166 where
167 S: ManifestStore,
168 {
169 self.prolly.load_named_root(&self.root_name(id))
170 }
171
172 pub fn load_many<I, Id>(&self, ids: I) -> Result<SnapshotSelection, Error>
174 where
175 S: ManifestStore,
176 I: IntoIterator<Item = Id>,
177 Id: AsRef<[u8]>,
178 {
179 let ids = ids
180 .into_iter()
181 .map(|id| id.as_ref().to_vec())
182 .collect::<Vec<_>>();
183 let names = ids.iter().map(|id| self.root_name(id)).collect::<Vec<_>>();
184 let selection = self.prolly.load_named_roots(names)?;
185 Ok(self.selection_from_named_roots(selection))
186 }
187
188 pub fn publish(&self, id: impl AsRef<[u8]>, tree: &Tree) -> Result<(), Error>
190 where
191 S: ManifestStore,
192 {
193 self.prolly.publish_named_root(&self.root_name(id), tree)
194 }
195
196 pub fn publish_at_millis(
198 &self,
199 id: impl AsRef<[u8]>,
200 tree: &Tree,
201 timestamp_millis: u64,
202 ) -> Result<(), Error>
203 where
204 S: ManifestStore,
205 {
206 self.prolly
207 .publish_named_root_at_millis(&self.root_name(id), tree, timestamp_millis)
208 }
209
210 pub fn delete(&self, id: impl AsRef<[u8]>) -> Result<(), Error>
212 where
213 S: ManifestStore,
214 {
215 self.prolly.delete_named_root(&self.root_name(id))
216 }
217
218 pub fn compare_and_swap(
220 &self,
221 id: impl AsRef<[u8]>,
222 expected: Option<&Tree>,
223 replacement: Option<&Tree>,
224 ) -> Result<NamedRootUpdate, Error>
225 where
226 S: ManifestStore,
227 {
228 self.prolly
229 .compare_and_swap_named_root(&self.root_name(id), expected, replacement)
230 }
231
232 pub fn compare_and_swap_at_millis(
234 &self,
235 id: impl AsRef<[u8]>,
236 expected: Option<&Tree>,
237 replacement: Option<&Tree>,
238 timestamp_millis: u64,
239 ) -> Result<NamedRootUpdate, Error>
240 where
241 S: ManifestStore,
242 {
243 self.prolly.compare_and_swap_named_root_at_millis(
244 &self.root_name(id),
245 expected,
246 replacement,
247 timestamp_millis,
248 )
249 }
250
251 pub fn list(&self) -> Result<Vec<SnapshotRoot>, Error>
253 where
254 S: ManifestStoreScan,
255 {
256 let mut snapshots = self
257 .prolly
258 .list_named_root_manifests()?
259 .into_iter()
260 .filter_map(|root| {
261 SnapshotRoot::from_manifest(&self.namespace, root.name, root.manifest)
262 })
263 .collect::<Vec<_>>();
264 snapshots.sort_by(|left, right| left.name.cmp(&right.name));
265 Ok(snapshots)
266 }
267
268 fn selection_from_named_roots(&self, selection: NamedRootSelection) -> SnapshotSelection {
269 let snapshots = selection
270 .roots
271 .into_iter()
272 .filter_map(|root| {
273 let id = self.namespace.id_from_name(&root.name)?;
274 Some(SnapshotRoot {
275 id,
276 name: root.name,
277 tree: root.tree,
278 created_at_millis: None,
279 updated_at_millis: None,
280 })
281 })
282 .collect::<Vec<_>>();
283 let missing_ids = selection
284 .missing_names
285 .into_iter()
286 .filter_map(|name| self.namespace.id_from_name(name))
287 .collect::<Vec<_>>();
288 SnapshotSelection {
289 snapshots,
290 missing_ids,
291 }
292 }
293}
294
295impl<S: Store> Prolly<S> {
296 pub fn snapshots(&self, namespace: SnapshotNamespace) -> SnapshotManager<'_, S> {
298 SnapshotManager::new(self, namespace)
299 }
300
301 pub fn branch_snapshots(&self) -> SnapshotManager<'_, S> {
303 self.snapshots(SnapshotNamespace::Branch)
304 }
305
306 pub fn tag_snapshots(&self) -> SnapshotManager<'_, S> {
308 self.snapshots(SnapshotNamespace::Tag)
309 }
310
311 pub fn checkpoint_snapshots(&self) -> SnapshotManager<'_, S> {
313 self.snapshots(SnapshotNamespace::Checkpoint)
314 }
315}