use std::path::Path;
use fs4::fs_std::FileExt;
pub(crate) fn with_exclusive_lock<F, R, E>(lock_path: &Path, f: F) -> Result<R, E>
where
F: FnOnce() -> Result<R, E>,
E: From<LockError>,
{
if let Some(parent) = lock_path.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| LockError::CreateDir {
path: parent.to_path_buf(),
source: e,
})
.map_err(E::from)?;
}
let file = std::fs::OpenOptions::new()
.create(true)
.read(true)
.write(true)
.truncate(false)
.open(lock_path)
.map_err(|e| LockError::Open {
path: lock_path.to_path_buf(),
source: e,
})
.map_err(E::from)?;
FileExt::lock_exclusive(&file)
.map_err(|e| LockError::Acquire {
path: lock_path.to_path_buf(),
source: e,
})
.map_err(E::from)?;
let result = f();
drop(file);
result
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum LockError {
#[error("failed to create lock dir {}: {source}", path.display())]
CreateDir {
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
#[error("failed to open lock file {}: {source}", path.display())]
Open {
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
#[error("failed to acquire lock on {}: {source}", path.display())]
Acquire {
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
}
impl From<LockError> for String {
fn from(e: LockError) -> Self {
e.to_string()
}
}