1use std::fs;
7use std::io::{ErrorKind, Write};
8use std::path::PathBuf;
9
10use anyhow::{Context, Result, bail};
11
12use crate::git;
13
14const LOCK_FILE: &str = "stk-lock";
15
16pub struct Lock {
19 path: Option<PathBuf>,
20}
21
22impl Lock {
23 pub fn acquire(command: &str) -> Result<Self> {
26 let Ok(path) = git::git_common_path(LOCK_FILE) else {
27 return Ok(Self { path: None });
30 };
31 let path = PathBuf::from(path);
32
33 match fs::OpenOptions::new()
34 .write(true)
35 .create_new(true)
36 .open(&path)
37 {
38 Ok(mut file) => {
39 let _ = writeln!(file, "{} {command}", std::process::id());
41 Ok(Self { path: Some(path) })
42 }
43 Err(error) if error.kind() == ErrorKind::AlreadyExists => {
44 let holder = fs::read_to_string(&path).unwrap_or_default();
45 let holder = holder.trim();
46 let by = if holder.is_empty() {
47 String::new()
48 } else {
49 format!(" ({holder})")
50 };
51 bail!(
52 "another git stk operation is in progress{by}; wait for it to \
53 finish, or remove {} if it is stale",
54 path.display()
55 );
56 }
57 Err(error) => {
58 Err(error).with_context(|| format!("failed to take the lock at {}", path.display()))
59 }
60 }
61 }
62}
63
64impl Drop for Lock {
65 fn drop(&mut self) {
66 if let Some(path) = &self.path {
67 let _ = fs::remove_file(path);
68 }
69 }
70}