spec_driven_docs/landing/
lock.rs1use camino::{Utf8Path, Utf8PathBuf};
4
5use crate::domain::ownership::Sha256;
6use crate::domain::paths::UserEnv;
7use crate::error::AppError;
8use crate::transaction::lock::Lock;
9
10pub fn state_root() -> Result<Utf8PathBuf, AppError> {
16 Ok(UserEnv::from_process()
17 .state_root()
18 .ok_or_else(|| AppError::Usage("no state root resolves".to_string()))?
19 .path)
20}
21
22pub fn path_for(target: &Utf8Path) -> Result<Utf8PathBuf, AppError> {
32 let key = Sha256::of(target.as_str().as_bytes());
33 Ok(state_root()?.join("locks").join(format!("{key}.lock")))
34}
35
36pub fn hold(target: &Utf8Path) -> Result<Lock, AppError> {
43 Lock::exclusive(&path_for(target)?, "landing")
44}
45
46#[cfg(test)]
47mod tests {
48 #![allow(
49 clippy::unwrap_used,
50 reason = "a test panics as its failure signal, not as control flow"
51 )]
52
53 use super::*;
54
55 #[test]
56 fn two_targets_take_two_locks_and_neither_names_a_directory() {
57 let one = path_for(Utf8Path::new("/work/one")).unwrap();
58 let two = path_for(Utf8Path::new("/work/two")).unwrap();
59 assert_ne!(one, two);
60 assert!(!one.as_str().contains("work"), "{one}");
61 }
62}