Skip to content

Commit 074526b

Browse files
implement primitive file-based project lock
1 parent 09d8942 commit 074526b

File tree

4 files changed

+85
-93
lines changed

4 files changed

+85
-93
lines changed

Cargo.lock

Lines changed: 33 additions & 79 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ indicatif = { version = "0.17.8", features = ["improved_unicode", "tokio"] }
1919
# async runtime and helpers
2020
futures = "0.3"
2121
futures-util = "0.3"
22-
tokio = { version = "1.34", features = ["macros", "process", "rt-multi-thread"]}
22+
tokio = { version = "1.34", features = [
23+
"macros",
24+
"process",
25+
"rt-multi-thread",
26+
] }
2327
tokio-util = { version = "0.7", features = ["io"] }
2428
async-compression = { version = "0.4", features = ["futures-io", "gzip"] }
2529

@@ -45,3 +49,6 @@ log = "0.4.21"
4549

4650
[target.'cfg(windows)'.dependencies]
4751
winreg = "0.50"
52+
53+
[dev-dependencies]
54+
tempfile = "3.10"

src/server/lock.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,61 @@
11
use std::fs::{self, File};
22
use std::path::{Path, PathBuf};
33

4-
#[derive(thiserror::Error, Debug)]
5-
pub enum Error {
6-
#[error("The lock at {0:?} is already acquired by a process with PID {1}.")]
7-
InUse(PathBuf, u32),
8-
}
9-
104
/// Only one server process can "own" a project at a single time.
115
/// We enforce this exclusive access through the creation and deletion of this lockfile.
126
const LOCKFILE: &'static str = ".server-lock";
137

14-
pub struct ProjectLock<'a> {
8+
pub struct ProjectLock {
159
file: File,
16-
path: &'a Path,
10+
path: PathBuf,
1711
}
1812

19-
impl<'a> ProjectLock<'_> {
13+
impl ProjectLock {
2014
/// Attempt to acquire a lock on the provided directory.
21-
pub fn lock(project_path: &'a Path) -> Result<Self, Error> {
22-
todo!()
15+
pub fn lock(project_path: &Path) -> Option<Self> {
16+
let lock = project_path.join(LOCKFILE);
17+
match lock.is_file() {
18+
true => None,
19+
false => {
20+
let file = File::create(&lock).ok()?;
21+
Some(Self { file, path: lock })
22+
}
23+
}
2324
}
2425
}
2526

26-
impl Drop for ProjectLock<'_> {
27+
impl Drop for ProjectLock {
2728
fn drop(&mut self) {
2829
// The file handle is dropped before this, so we can safely delete it.
29-
fs::remove_file(self.path);
30+
fs::remove_file(&self.path).unwrap();
31+
}
32+
}
33+
34+
#[cfg(test)]
35+
mod test {
36+
use super::*;
37+
use tempfile::TempDir;
38+
39+
/// Test that project locks behave in the following way:
40+
/// - Attempting to lock an already locked project MUST return None.
41+
/// - Attempting to lock a project that is not locked will return a ProjectLock.
42+
/// - Dropping a ProjectLock will remove the lockfile from the project.
43+
#[test]
44+
fn test_project_lock() {
45+
let temp = TempDir::new().unwrap();
46+
let root = temp.path();
47+
48+
// Acquire a lock on the directory.
49+
{
50+
let _lock =
51+
ProjectLock::lock(root).expect("Failed to acquire lock on empty directory.");
52+
53+
// Attempting to acquire it again will return None.
54+
let relock = ProjectLock::lock(root);
55+
assert!(relock.is_none());
56+
}
57+
58+
// Now that the previous lock is dropped we *should* be able to re-acquire it.
59+
let _lock = ProjectLock::lock(root).expect("Failed to acquire lock on empty directory.");
3060
}
3161
}

src/server/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{io, thread};
55

66
use self::proto::{Message, Request, Response};
77

8+
mod lock;
89
mod method;
910
mod proto;
1011
mod route;

0 commit comments

Comments
 (0)