use std::collections::HashSet;
use crate::tree::Hash;
use super::commit::BranchCommitError;
use super::handle::{BranchError, BranchHandle, RefBinding, ShardId};
use super::policy::BranchKind;
use super::refstore::{BranchRefError, BranchRefRecord, BranchRefStore, BranchShardRef};
use super::registry::BranchRegistry;
use super::snapshot::{SnapshotRegistry, Timestamp};
pub fn fork_from(
parent: &BranchHandle,
registry: &BranchRegistry,
) -> Result<BranchHandle, BranchError> {
if parent.registry_guard().is_none() {
return Err(BranchError::UnregisteredParent);
}
let states = parent.lock_states_ascending()?;
let heads: Vec<(ShardId, Hash)> = states
.iter()
.map(|(shard_id, state)| (*shard_id, state.current_root))
.collect();
let child = BranchHandle::from_shard_roots(heads)?;
let guard = registry.register_roots(child.fork_points().flat_map(|anchor| [anchor, anchor]));
drop(states);
Ok(child.with_registry_guard(guard))
}
pub fn fork_at(
root: Hash,
registry: &BranchRegistry,
refs: &BranchRefStore,
snapshots: &SnapshotRegistry,
) -> Result<BranchHandle, BranchCommitError> {
let durable = refs.protected_roots();
let named: HashSet<Hash> = snapshots
.list_snapshots()
.into_iter()
.map(|(_name, root_hash, _timestamp)| root_hash)
.collect();
let guard = registry
.register_roots_protected([root, root], |anchor| {
durable.contains(anchor) || named.contains(anchor)
})
.map_err(|root| BranchCommitError::UnprotectedAnchor { root })?;
Ok(BranchHandle::new(root).with_registry_guard(guard))
}
pub fn create_branch<I>(
name: &str,
roots: I,
refs: &mut BranchRefStore,
registry: &BranchRegistry,
timestamp: Timestamp,
) -> Result<BranchHandle, BranchCommitError>
where
I: IntoIterator<Item = (ShardId, Hash)>,
{
create_branch_record(
name,
roots,
BranchKind::Work,
None,
refs,
registry,
timestamp,
)
}
pub(crate) fn create_branch_record<I>(
name: &str,
roots: I,
kind: BranchKind,
namespace_lineage: Option<String>,
refs: &mut BranchRefStore,
registry: &BranchRegistry,
timestamp: Timestamp,
) -> Result<BranchHandle, BranchCommitError>
where
I: IntoIterator<Item = (ShardId, Hash)>,
{
let roots: Vec<(ShardId, Hash)> = roots.into_iter().collect();
let handle = BranchHandle::from_shard_roots(roots.iter().copied())?;
let guard = registry.register_roots(handle.fork_points().flat_map(|anchor| [anchor, anchor]));
refs.create(BranchRefRecord {
name: name.to_owned(),
created: timestamp,
kind,
namespace_lineage,
seq: 0,
timestamp,
shards: roots
.iter()
.map(|&(shard_id, root)| BranchShardRef {
shard_id,
fork_anchor: root,
head: root,
})
.collect(),
parents: Vec::new(),
})?;
Ok(handle.with_registry_guard(guard).with_binding(RefBinding {
name: name.to_owned(),
created: timestamp,
seq: 0,
}))
}
pub fn open_branch(
name: &str,
refs: &BranchRefStore,
registry: &BranchRegistry,
) -> Result<BranchHandle, BranchCommitError> {
let Some(record) = refs.get(name) else {
return Err(BranchCommitError::Ref(BranchRefError::BranchRemoved(
name.to_owned(),
)));
};
let handle = BranchHandle::from_anchor_head_pairs(
record
.shards
.iter()
.map(|shard| (shard.shard_id, shard.fork_anchor, shard.head)),
)?;
let guard = registry.register_roots(
record
.shards
.iter()
.flat_map(|shard| [shard.fork_anchor, shard.head]),
);
Ok(handle.with_registry_guard(guard).with_binding(RefBinding {
name: record.name.clone(),
created: record.created,
seq: record.seq,
}))
}
pub fn remove_branch(
name: &str,
refs: &mut BranchRefStore,
) -> Result<Option<BranchRefRecord>, BranchRefError> {
refs.remove(name)
}
#[cfg(test)]
#[path = "lifecycle_tests.rs"]
mod tests;