use std::fs::{File, OpenOptions, TryLockError};
use std::io;
use std::path::Path;
#[derive(Debug)]
pub struct ProcessLock(File);
impl ProcessLock {
pub fn try_acquire(path: impl AsRef<Path>) -> io::Result<Option<Self>> {
let mut options = OpenOptions::new();
options.read(true).write(true).create(true).truncate(false);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.mode(0o600);
}
Self::try_from_file(options.open(path)?)
}
pub fn try_from_file(file: File) -> io::Result<Option<Self>> {
match file.try_lock() {
Ok(()) => Ok(Some(Self(file))),
Err(TryLockError::WouldBlock) => Ok(None),
Err(TryLockError::Error(error)) => Err(error),
}
}
pub fn is_held(path: impl AsRef<Path>) -> io::Result<bool> {
let file = match OpenOptions::new().read(true).write(true).open(path) {
Ok(file) => file,
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),
Err(error) => return Err(error),
};
Ok(Self::try_from_file(file)?.is_none())
}
}
impl Drop for ProcessLock {
fn drop(&mut self) {
let _ = self.0.unlock();
}
}