Skip to main content

linkrs_migration/
lock.rs

1use std::fs::{File, OpenOptions};
2use std::path::Path;
3
4use fs4::fs_std::FileExt;
5use linkrs_core::StorageError;
6
7use crate::error::MigrationError;
8
9pub struct MigrationFileLock {
10    _file: File,
11    path: std::path::PathBuf,
12}
13
14impl MigrationFileLock {
15    pub fn try_acquire(path: &Path) -> Result<Self, MigrationError> {
16        let file = OpenOptions::new()
17            .write(true)
18            .create(true)
19            .truncate(true)
20            .open(path)
21            .map_err(|e| MigrationError::Storage(Box::new(StorageError::from(e))))?;
22
23        file.try_lock_exclusive().map_err(|e| {
24            MigrationError::Lock(format!(
25                "migration in progress (lock file: {}): {}",
26                path.display(),
27                e
28            ))
29        })?;
30
31        Ok(Self {
32            _file: file,
33            path: path.to_path_buf(),
34        })
35    }
36}
37
38impl Drop for MigrationFileLock {
39    fn drop(&mut self) {
40        // Fully-qualified to avoid `unstable_name_collisions` once std adds
41        // `File::unlock`.
42        let _ = fs4::fs_std::FileExt::unlock(&self._file);
43        let _ = std::fs::remove_file(&self.path);
44    }
45}
46
47impl std::fmt::Debug for MigrationFileLock {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.debug_struct("MigrationFileLock")
50            .field("path", &self.path)
51            .finish()
52    }
53}