use crate::*;
use std::{
fs::{File, OpenOptions},
path::PathBuf,
process,
};
use fs2::FileExt;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub struct FileLock {
id: FileLockId,
path: PathBuf,
file: File,
have_lock: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
struct FileLockId {
pid: u32,
uid: Uuid,
}
impl Default for FileLockId {
fn default() -> Self {
FileLockId {
pid: process::id(),
uid: Uuid::new_v4(),
}
}
}
impl FileLock {
pub fn new<P: Into<PathBuf>>(pathbuf: P) -> SimpleLockResult<FileLock> {
let path = pathbuf.into();
let id = FileLockId::default();
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.map_err(|e| SimpleLockError::from(e))?;
Ok(FileLock {
id,
path,
file,
have_lock: None,
})
}
pub fn path(&self) -> PathBuf {
self.path.clone()
}
}
impl ConcreteLock for FileLock {
fn status(&self) -> SimpleLockResult<LockStatus> {
match self.have_lock {
Some(true) => Ok(LockStatus::Mine),
_ => {
if self.file.allocated_size().map_or(false, |v| v > 0) {
Ok(LockStatus::Taken)
} else {
Ok(LockStatus::Open)
}
}
}
}
fn try_lock(&mut self) -> SimpleLockResult<()> {
if self.have_lock.unwrap_or(false) {
return Ok(());
}
self.file
.try_lock_exclusive()
.and_then(|_| serde_json::to_writer(&self.file, &self.id).map_err(|e| e.into()))
.or_else(|e| Err(e.into()))
.and_then(|_| {
self.have_lock = Some(true);
Ok(())
})
}
fn hang_lock(&mut self) -> SimpleLockResult<()> {
if self.have_lock.unwrap_or(false) {
return Ok(());
}
self.file
.lock_exclusive()
.and_then(|_| serde_json::to_writer(&self.file, &self.id).map_err(|e| e.into()))
.or_else(|e| Err(e.into()))
.and_then(|_| {
self.have_lock = Some(true);
Ok(())
})
}
fn try_unlock(&mut self) -> SimpleLockResult<()> {
if self.have_lock.unwrap_or(true) {
return Ok(());
}
self.file.set_len(0)?;
self.file.unlock().map_err(|e| e.into())
}
}