1use super::{Lock, LockError};
6
7#[cfg(not(target_arch = "wasm32"))]
8#[derive(Debug)]
9pub struct FsLock {
10 file: parking_lot::Mutex<Option<std::fs::File>>,
11 path: std::path::PathBuf,
12 state: parking_lot::Mutex<State>,
13}
14
15#[cfg(not(target_arch = "wasm32"))]
16#[derive(Debug)]
17enum State {
18 Unlocked,
19 Exclusive,
20}
21
22#[cfg(not(target_arch = "wasm32"))]
23impl FsLock {
24 pub fn new(path: std::path::PathBuf) -> Self {
25 Self {
26 file: parking_lot::Mutex::new(None),
27 path,
28 state: parking_lot::Mutex::new(State::Unlocked),
29 }
30 }
31}
32
33#[cfg(not(target_arch = "wasm32"))]
34impl Lock for FsLock {
35 fn try_exclusive(&self) -> Result<(), LockError> {
36 use fs2::FileExt;
37 use std::fs::OpenOptions;
38
39 let file = OpenOptions::new()
40 .read(true)
41 .write(true)
42 .create(true)
43 .truncate(true)
44 .open(&self.path)?;
45
46 file.try_lock_exclusive()?;
47
48 *self.state.lock() = State::Exclusive;
49 *self.file.lock() = Some(file);
50
51 Ok(())
52 }
53}
54
55#[derive(Debug, Default)]
56pub struct MemLock;
57
58impl Lock for MemLock {
59 fn try_exclusive(&self) -> Result<(), LockError> {
60 Ok(())
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::{FsLock, Lock};
67
68 #[test]
69 fn creates_an_exclusive_repo_lock() {
70 let temp_dir = std::env::temp_dir();
71 let lockfile_path = temp_dir.join("repo_lock");
72
73 let lock = FsLock::new(lockfile_path.clone());
74 let result = lock.try_exclusive();
75 assert!(result.is_ok());
76
77 let failing_lock = FsLock::new(lockfile_path.clone());
78 let result = failing_lock.try_exclusive();
79 assert!(result.is_err());
80
81 std::fs::remove_file(lockfile_path).unwrap();
83 }
84}