use crate::subvolume_error::MigrateError;
use crate::subvolume_journal::Journal;
use std::io::Write;
use std::path::{Path, PathBuf};
const LOCK_NAME: &str = "index.lock";
pub fn token_of(journal: &Journal) -> String {
return format!("cli-git migrate-to-subvolume {}\n", journal.started());
}
pub fn lock_paths(tree: &Path) -> Vec<PathBuf> {
let git_directory: PathBuf = tree.join(".git");
let mut found: Vec<PathBuf> = vec![git_directory.join(LOCK_NAME)];
if let Ok(names) = std::fs::read_dir(git_directory.join("worktrees")) {
for name in names.flatten() {
if name.file_type().is_ok_and(|kind: std::fs::FileType| { return kind.is_dir(); }) {
found.push(name.path().join(LOCK_NAME));
}
}
}
found.sort();
return found;
}
pub fn relative_lock_paths(tree: &Path) -> Vec<PathBuf> {
return lock_paths(tree)
.into_iter()
.filter_map(|path: PathBuf| { return path.strip_prefix(tree).ok().map(Path::to_path_buf); })
.collect();
}
pub fn is_ours(lock: &Path, token: &str) -> bool {
return std::fs::read(lock).is_ok_and(|content: Vec<u8>| { return content == token.as_bytes(); });
}
pub fn acquire(lock: &Path, token: &str) -> Result<(), MigrateError> {
match std::fs::OpenOptions::new().write(true).create_new(true).open(lock) {
Ok(mut file) => {
let written: std::io::Result<()> = file.write_all(token.as_bytes()).and_then(|()| { return file.sync_all(); });
return written.map_err(|error: std::io::Error| { return MigrateError::io("write the lock", lock, &error); });
}
Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {
if is_ours(lock, token) {
return Ok(());
}
return Err(MigrateError::Journal(format!(
"{} exists and is not this migration's lock: a Git command is writing that index. Wait for it to finish, or remove the file if no Git command is running, then run the command again",
lock.display()
)));
}
Err(error) => return Err(MigrateError::io("create the lock", lock, &error)),
}
}
pub fn release_all(tree: &Path, token: &str) -> Result<usize, MigrateError> {
let mut released: usize = 0;
for lock in lock_paths(tree) {
if !is_ours(lock.as_path(), token) {
continue;
}
match std::fs::remove_file(&lock) {
Ok(()) => released += 1,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => return Err(MigrateError::io("remove", lock.as_path(), &error)),
}
}
return Ok(released);
}
#[cfg(test)]
#[path = "subvolume_lock_tests.rs"]
mod tests;