1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! Global lock sentinel: a filesystem flag that halts all routine scheduling and triggers without
//! touching individual routine `enabled` states.
//!
//! Two variants live in `~/.config/moadim/`:
//!
//! - `.lock` — committed; shareable across machines via version control.
//! - `.local.lock` — gitignored (the `.local.` infix matches `*.local.*`); machine-local.
//!
//! Either file's *presence* activates the lock — content is ignored. Removing both files restores
//! prior scheduling state without changing any routine's `enabled` flag.
use std::io;
/// Which lock sentinel to create or remove.
///
/// `Copy` because it is a plain tag with no payload — passing it by value (the natural way to
/// pass an enum selector) then costs nothing, whereas passing it by reference would just add a
/// pointless indirection at every call site.
#[derive(Clone, Copy)]
pub enum LockScope {
/// The committed `.lock` file, shareable via version control.
Shared,
/// The gitignored `.local.lock` file, local to this machine.
Local,
}
/// Current state of both lock sentinels.
#[derive(serde::Serialize, utoipa::ToSchema)]
pub struct LockStatus {
/// Whether the committed `.lock` file is present.
pub shared: bool,
/// Whether the gitignored `.local.lock` file is present.
pub local: bool,
/// `true` if either sentinel is present (all routine scheduling and triggers halted).
pub locked: bool,
}
/// Returns `true` if either the shared (`.lock`) or local (`.local.lock`) sentinel exists.
pub fn is_globally_locked() -> bool {
crate::paths::global_lock_path().exists() || crate::paths::global_local_lock_path().exists()
}
/// Returns the current presence state of both lock sentinels.
pub fn lock_status() -> LockStatus {
let shared = crate::paths::global_lock_path().exists();
let local = crate::paths::global_local_lock_path().exists();
LockStatus {
shared,
local,
locked: shared || local,
}
}
/// Create or remove a lock sentinel.
///
/// Creating a lock writes an empty file (the daemon only checks for *presence*). Removing a lock
/// deletes the file if present; if the file is already absent this is a no-op.
pub fn set_lock(scope: LockScope, locked: bool) -> io::Result<()> {
let path = match scope {
LockScope::Shared => crate::paths::global_lock_path(),
LockScope::Local => crate::paths::global_local_lock_path(),
};
if locked {
crate::utils::fs_perms::create_private_dir_all(&crate::paths::config_dir())?;
std::fs::write(&path, b"")?;
} else if path.exists() {
std::fs::remove_file(&path)?;
}
Ok(())
}
#[cfg(test)]
#[path = "global_lock_tests.rs"]
mod tests;