use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use super::{CLAIMED_DIR, EXT, gestures_dir, list};
pub fn claimed(state_root: &Path) -> Vec<(String, PathBuf)> {
list(&gestures_dir(state_root).join(CLAIMED_DIR))
}
#[derive(Debug)]
pub struct Claim {
path: PathBuf,
lock: fs::File,
}
impl Drop for Claim {
fn drop(&mut self) {
let _ = self.lock.unlock();
}
}
impl Claim {
pub fn path(&self) -> PathBuf {
self.path.clone()
}
}
pub fn claim(state_root: &Path, id: &str) -> io::Result<Claim> {
let dir = gestures_dir(state_root);
let claimed_dir = dir.join(CLAIMED_DIR);
fs::create_dir_all(&claimed_dir)?;
let from = dir.join(format!("{id}{EXT}"));
let file = fs::File::open(&from)?;
if file.try_lock().is_err() {
return Err(io::Error::new(
io::ErrorKind::WouldBlock,
format!("gesture {id:?} is already claimed"),
));
}
let to = claimed_dir.join(format!("{id}{EXT}"));
fs::rename(&from, &to)?;
Ok(Claim {
path: to,
lock: file,
})
}
pub fn unheld(path: &Path) -> bool {
let Ok(file) = fs::File::open(path) else {
return false;
};
if file.try_lock().is_err() {
return false;
}
let _ = file.unlock();
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::boundary::deposit::deposit;
use serde_json::json;
use tempfile::tempdir;
#[test]
fn a_dropped_claim_releases_while_a_copy_of_its_descriptor_still_lives() {
let root = tempdir().unwrap();
deposit(root.path(), "g-1", &json!({"op": "ack"})).unwrap();
let held = claim(root.path(), "g-1").unwrap();
let path = held.path();
let forked = held.lock.try_clone().unwrap();
drop(held);
assert!(unheld(&path), "the drop released the lock, not the close");
drop(forked);
}
}