use std::fs::{File, OpenOptions};
use std::sync::Mutex;
use anyhow::{Context, Result};
use crate::profile::clauth_dir;
const LOCK_FILENAME: &str = ".lock";
struct Inner {
file: Option<File>,
depth: u32,
}
static LOCK: Mutex<Inner> = Mutex::new(Inner {
file: None,
depth: 0,
});
pub(crate) struct StateLock {
_private: (),
}
impl StateLock {
pub(crate) fn acquire() -> Result<Self> {
let mut guard = LOCK.lock().expect("clauth state lock mutex poisoned");
if guard.depth == 0 {
let dir = clauth_dir()?;
std::fs::create_dir_all(&dir).context("Failed to create ~/.clauth")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(dir.join(LOCK_FILENAME))
.context("Failed to open clauth state lock file")?;
file.lock().context("Failed to acquire clauth state lock")?;
guard.file = Some(file);
}
guard.depth = guard
.depth
.checked_add(1)
.expect("clauth state lock depth overflow");
Ok(Self { _private: () })
}
}
impl Drop for StateLock {
fn drop(&mut self) {
let mut guard = match LOCK.lock() {
Ok(g) => g,
Err(p) => p.into_inner(),
};
guard.depth = guard.depth.saturating_sub(1);
if guard.depth == 0 {
guard.file = None;
}
}
}
pub(crate) fn with_state_lock<T>(f: impl FnOnce() -> Result<T>) -> Result<T> {
let _guard = StateLock::acquire()?;
f()
}