use std::fmt;
use std::fs::{File, OpenOptions};
use std::io;
use std::path::{Path, PathBuf};
use fs4::FileExt;
const LOCK_FILE: &str = "lock";
#[derive(Debug)]
pub struct DataDirLock {
path: PathBuf,
_file: File,
}
impl DataDirLock {
pub fn path(&self) -> &Path {
&self.path
}
}
pub fn acquire(data_dir: &Path) -> Result<DataDirLock, LockError> {
let path = data_dir.join(LOCK_FILE);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(&path)
.map_err(|source| LockError::Open {
path: path.clone(),
source,
})?;
FileExt::try_lock(&file).map_err(|err| match err {
fs4::TryLockError::WouldBlock => LockError::Held { path: path.clone() },
fs4::TryLockError::Error(source) => LockError::Open {
path: path.clone(),
source,
},
})?;
Ok(DataDirLock { path, _file: file })
}
#[derive(Debug)]
pub enum LockError {
Open { path: PathBuf, source: io::Error },
Held { path: PathBuf },
}
impl fmt::Display for LockError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LockError::Open { path, source } => {
write!(f, "cannot open the lock file {}: {source}", path.display())
}
LockError::Held { path } => write!(
f,
"another instance holds the data-directory lock {}",
path.display()
),
}
}
}
impl std::error::Error for LockError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
LockError::Open { source, .. } => Some(source),
LockError::Held { .. } => None,
}
}
}