use super::error::Error;
use super::manifest::{
ManifestStore, ManifestStoreScan, NamedRootSelection, NamedRootUpdate, RootManifest,
};
use super::store::Store;
use super::tree::Tree;
use super::Prolly;
pub const SNAPSHOT_BRANCH_PREFIX: &[u8] = b"refs/heads/";
pub const SNAPSHOT_TAG_PREFIX: &[u8] = b"refs/tags/";
pub const SNAPSHOT_CHECKPOINT_PREFIX: &[u8] = b"refs/checkpoints/";
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SnapshotNamespace {
Branch,
Tag,
Checkpoint,
Custom(Vec<u8>),
}
impl SnapshotNamespace {
pub fn branch() -> Self {
Self::Branch
}
pub fn tag() -> Self {
Self::Tag
}
pub fn checkpoint() -> Self {
Self::Checkpoint
}
pub fn custom(prefix: impl Into<Vec<u8>>) -> Self {
Self::Custom(prefix.into())
}
pub fn prefix(&self) -> &[u8] {
match self {
Self::Branch => SNAPSHOT_BRANCH_PREFIX,
Self::Tag => SNAPSHOT_TAG_PREFIX,
Self::Checkpoint => SNAPSHOT_CHECKPOINT_PREFIX,
Self::Custom(prefix) => prefix,
}
}
pub fn root_name(&self, id: impl AsRef<[u8]>) -> Vec<u8> {
snapshot_root_name(self, id)
}
pub fn id_from_name(&self, name: impl AsRef<[u8]>) -> Option<Vec<u8>> {
snapshot_id_from_name(self, name)
}
}
pub fn snapshot_root_name(namespace: &SnapshotNamespace, id: impl AsRef<[u8]>) -> Vec<u8> {
let prefix = namespace.prefix();
let id = id.as_ref();
let mut name = Vec::with_capacity(prefix.len() + id.len());
name.extend_from_slice(prefix);
name.extend_from_slice(id);
name
}
pub fn snapshot_id_from_name(
namespace: &SnapshotNamespace,
name: impl AsRef<[u8]>,
) -> Option<Vec<u8>> {
let prefix = namespace.prefix();
let name = name.as_ref();
name.strip_prefix(prefix).map(<[u8]>::to_vec)
}
#[derive(Clone, Debug, PartialEq)]
pub struct SnapshotRoot {
pub id: Vec<u8>,
pub name: Vec<u8>,
pub tree: Tree,
pub created_at_millis: Option<u64>,
pub updated_at_millis: Option<u64>,
}
impl SnapshotRoot {
fn from_manifest(
namespace: &SnapshotNamespace,
name: Vec<u8>,
manifest: RootManifest,
) -> Option<Self> {
let id = snapshot_id_from_name(namespace, &name)?;
let created_at_millis = manifest.created_at_millis;
let updated_at_millis = manifest.updated_at_millis;
Some(Self {
id,
name,
tree: manifest.into_tree(),
created_at_millis,
updated_at_millis,
})
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SnapshotSelection {
pub snapshots: Vec<SnapshotRoot>,
pub missing_ids: Vec<Vec<u8>>,
}
pub struct SnapshotManager<'a, S: Store> {
prolly: &'a Prolly<S>,
namespace: SnapshotNamespace,
}
impl<'a, S: Store> SnapshotManager<'a, S> {
pub fn new(prolly: &'a Prolly<S>, namespace: SnapshotNamespace) -> Self {
Self { prolly, namespace }
}
pub fn namespace(&self) -> &SnapshotNamespace {
&self.namespace
}
pub fn root_name(&self, id: impl AsRef<[u8]>) -> Vec<u8> {
self.namespace.root_name(id)
}
pub fn load(&self, id: impl AsRef<[u8]>) -> Result<Option<Tree>, Error>
where
S: ManifestStore,
{
self.prolly.load_named_root(&self.root_name(id))
}
pub fn load_many<I, Id>(&self, ids: I) -> Result<SnapshotSelection, Error>
where
S: ManifestStore,
I: IntoIterator<Item = Id>,
Id: AsRef<[u8]>,
{
let ids = ids
.into_iter()
.map(|id| id.as_ref().to_vec())
.collect::<Vec<_>>();
let names = ids.iter().map(|id| self.root_name(id)).collect::<Vec<_>>();
let selection = self.prolly.load_named_roots(names)?;
Ok(self.selection_from_named_roots(selection))
}
pub fn publish(&self, id: impl AsRef<[u8]>, tree: &Tree) -> Result<(), Error>
where
S: ManifestStore,
{
self.prolly.publish_named_root(&self.root_name(id), tree)
}
pub fn publish_at_millis(
&self,
id: impl AsRef<[u8]>,
tree: &Tree,
timestamp_millis: u64,
) -> Result<(), Error>
where
S: ManifestStore,
{
self.prolly
.publish_named_root_at_millis(&self.root_name(id), tree, timestamp_millis)
}
pub fn delete(&self, id: impl AsRef<[u8]>) -> Result<(), Error>
where
S: ManifestStore,
{
self.prolly.delete_named_root(&self.root_name(id))
}
pub fn compare_and_swap(
&self,
id: impl AsRef<[u8]>,
expected: Option<&Tree>,
replacement: Option<&Tree>,
) -> Result<NamedRootUpdate, Error>
where
S: ManifestStore,
{
self.prolly
.compare_and_swap_named_root(&self.root_name(id), expected, replacement)
}
pub fn compare_and_swap_at_millis(
&self,
id: impl AsRef<[u8]>,
expected: Option<&Tree>,
replacement: Option<&Tree>,
timestamp_millis: u64,
) -> Result<NamedRootUpdate, Error>
where
S: ManifestStore,
{
self.prolly.compare_and_swap_named_root_at_millis(
&self.root_name(id),
expected,
replacement,
timestamp_millis,
)
}
pub fn list(&self) -> Result<Vec<SnapshotRoot>, Error>
where
S: ManifestStoreScan,
{
let mut snapshots = self
.prolly
.list_named_root_manifests()?
.into_iter()
.filter_map(|root| {
SnapshotRoot::from_manifest(&self.namespace, root.name, root.manifest)
})
.collect::<Vec<_>>();
snapshots.sort_by(|left, right| left.name.cmp(&right.name));
Ok(snapshots)
}
fn selection_from_named_roots(&self, selection: NamedRootSelection) -> SnapshotSelection {
let snapshots = selection
.roots
.into_iter()
.filter_map(|root| {
let id = self.namespace.id_from_name(&root.name)?;
Some(SnapshotRoot {
id,
name: root.name,
tree: root.tree,
created_at_millis: None,
updated_at_millis: None,
})
})
.collect::<Vec<_>>();
let missing_ids = selection
.missing_names
.into_iter()
.filter_map(|name| self.namespace.id_from_name(name))
.collect::<Vec<_>>();
SnapshotSelection {
snapshots,
missing_ids,
}
}
}
impl<S: Store> Prolly<S> {
pub fn snapshots(&self, namespace: SnapshotNamespace) -> SnapshotManager<'_, S> {
SnapshotManager::new(self, namespace)
}
pub fn branch_snapshots(&self) -> SnapshotManager<'_, S> {
self.snapshots(SnapshotNamespace::Branch)
}
pub fn tag_snapshots(&self) -> SnapshotManager<'_, S> {
self.snapshots(SnapshotNamespace::Tag)
}
pub fn checkpoint_snapshots(&self) -> SnapshotManager<'_, S> {
self.snapshots(SnapshotNamespace::Checkpoint)
}
}