use std::{
fs::{self, File, OpenOptions},
io,
path::{Path, PathBuf},
};
pub(crate) fn edit_lock_path(path: &Path) -> PathBuf {
path.with_file_name(format!(
".{}.rho-edit.lock",
path.file_name()
.and_then(|name| name.to_str())
.unwrap_or("agent")
))
}
pub(crate) fn acquire_edit_lock(path: &Path) -> io::Result<EditFileLock> {
acquire_lock_file(&edit_lock_path(path))
}
pub(crate) fn acquire_lock_file(lock_path: &Path) -> io::Result<EditFileLock> {
if let Some(parent) = lock_path.parent() {
fs::create_dir_all(parent)?;
}
let mut options = OpenOptions::new();
options.read(true).write(true).create(true).truncate(false);
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
options.custom_flags(libc::O_NOFOLLOW);
}
let file = options.open(lock_path).map_err(|error| {
io::Error::new(error.kind(), format!("could not open edit lock: {error}"))
})?;
fs2::FileExt::try_lock_exclusive(&file).map_err(|error| {
io::Error::new(
error.kind(),
format!("could not lock file for editing: {error}"),
)
})?;
Ok(EditFileLock { file })
}
pub(crate) struct EditFileLock {
file: File,
}
impl EditFileLock {
#[cfg(test)]
pub(crate) fn inherited_handle_for_tests(&self) -> File {
self.file.try_clone().unwrap()
}
}
impl Drop for EditFileLock {
fn drop(&mut self) {
let _ = fs2::FileExt::unlock(&self.file);
}
}