use std::path::{Path, PathBuf};
use prikk_error::{PrikkError, Result};
use crate::fsutil::{MutationRoot, create_new_file_required, remove_file_cleanup_best_effort};
use crate::layout::{LockableContainer, RepositoryLayout};
#[derive(Debug)]
pub struct ActiveLock {
path: PathBuf,
relative: PathBuf,
mutation_root: MutationRoot,
}
impl ActiveLock {
pub fn acquire(layout: &RepositoryLayout) -> Result<Self> {
let path = layout.default_active_lock_path();
let relative = layout.repository_relative(&path)?;
let mutation_root = layout.repository_mutation_root().clone();
acquire_lock_file(&mutation_root, &relative, &path, "active")?;
Ok(Self {
path,
relative,
mutation_root,
})
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
pub(crate) fn require_layout(&self, layout: &RepositoryLayout) -> Result<()> {
if self
.mutation_root
.same_authority(layout.repository_mutation_root())
{
return Ok(());
}
Err(PrikkError::LockConflict(
"active lock belongs to a different repository authority".to_string(),
))
}
}
impl Drop for ActiveLock {
fn drop(&mut self) {
remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
}
}
#[derive(Debug)]
pub struct RefLock {
path: PathBuf,
relative: PathBuf,
mutation_root: MutationRoot,
}
impl RefLock {
pub fn acquire(layout: &RepositoryLayout, ref_name: &str) -> Result<Self> {
let path = layout.ref_lock_path(ref_name);
let relative = layout.repository_relative(&path)?;
let mutation_root = layout.repository_mutation_root().clone();
acquire_lock_file(&mutation_root, &relative, &path, "ref")?;
Ok(Self {
path,
relative,
mutation_root,
})
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
}
impl Drop for RefLock {
fn drop(&mut self) {
remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
}
}
#[derive(Debug)]
struct ContainerLockHandle {
relative: PathBuf,
mutation_root: MutationRoot,
}
impl Drop for ContainerLockHandle {
fn drop(&mut self) {
remove_file_cleanup_best_effort(&self.mutation_root, &self.relative);
}
}
#[derive(Debug)]
pub struct ContainerLockGuard {
_handles: Vec<ContainerLockHandle>,
}
pub fn acquire_container_locks(
layout: &RepositoryLayout,
containers: &[LockableContainer],
) -> Result<ContainerLockGuard> {
let mut sorted = containers.to_vec();
sorted.sort_unstable();
sorted.dedup();
let mutation_root = layout.repository_mutation_root().clone();
let mut handles = Vec::with_capacity(sorted.len());
for container in sorted {
let path = layout.lockable_container_lock_path(container);
let relative = layout.repository_relative(&path)?;
acquire_lock_file(
&mutation_root,
&relative,
&path,
container_lock_kind(container),
)?;
handles.push(ContainerLockHandle {
relative,
mutation_root: mutation_root.clone(),
});
}
Ok(ContainerLockGuard { _handles: handles })
}
fn container_lock_kind(container: LockableContainer) -> &'static str {
match container {
LockableContainer::RefPointerIndex => "container:ref-pointer-index",
LockableContainer::RefLog => "container:ref-log",
LockableContainer::ReceivedIndex => "container:received-index",
LockableContainer::TrustPolicy => "container:trust-policy",
}
}
fn acquire_lock_file(
mutation_root: &MutationRoot,
relative: &Path,
path: &Path,
kind: &str,
) -> Result<()> {
let body = lock_body(kind);
match create_new_file_required(mutation_root, relative, body.as_bytes()) {
Ok(()) => Ok(()),
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => Err(
PrikkError::LockConflict(format!("{kind} lock already exists: {}", path.display())),
),
Err(err) => Err(err.into()),
}
}
fn lock_body(kind: &str) -> String {
format!(
"pid={}\nkind={kind}\nnote=PR-007 lock has no stale-lock stealing yet\n",
std::process::id()
)
}
#[cfg(all(test, target_os = "linux"))]
mod tests;