use super::*;
use std::path::{Path, PathBuf};
pub(super) async fn handle_release_worktree_handles(
state: &SharedState,
path: &NormalizedPath,
) -> Response {
let target = match canonicalize_for_match(path.as_path()) {
Some(p) => p,
None => path.as_path().to_path_buf(),
};
let cache_root = match canonicalize_for_match(state.cache_dir.as_path()) {
Some(p) => p,
None => state.cache_dir.as_path().to_path_buf(),
};
if cache_root.starts_with(&target) {
return Response::Error {
message: format!(
"refusing to release handles under {} — that path contains the daemon \
cache root {}",
target.display(),
cache_root.display(),
),
};
}
let session_ids = state.sessions.active_ids();
let inspected = session_ids.len() as u32;
let mut released: u32 = 0;
let mut sessions_dropped: Vec<String> = Vec::new();
for sid in session_ids {
let Some(session) = state.sessions.get(&sid) else {
continue;
};
if !path_is_under(&session.working_dir, &target) {
continue;
}
state.session_worktree_roots.remove(&sid);
if let Some(ended) = state.sessions.end(&sid) {
if !ended.owner_pids.is_empty() {
state
.private_daemon
.release_session(&ended.owner_pids)
.await;
}
if let Some(ref journal_path) = ended.journal_path {
state.journal.close_session(journal_path);
}
released += 1;
sessions_dropped.push(sid.to_string());
}
}
tracing::info!(
path = %target.display(),
inspected,
released,
"released worktree handles"
);
Response::ReleaseWorktreeHandlesResult {
inspected,
released,
sessions_dropped,
unreleased: Vec::new(),
}
}
fn canonicalize_for_match(p: &Path) -> Option<PathBuf> {
std::fs::canonicalize(p).ok()
}
fn path_is_under(candidate: &NormalizedPath, target: &Path) -> bool {
let resolved = canonicalize_for_match(candidate.as_path())
.unwrap_or_else(|| candidate.as_path().to_path_buf());
resolved.starts_with(target)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn target_path_is_canonicalized_for_match() {
let bogus = std::path::Path::new(if cfg!(windows) {
r"C:\zccache-nonexistent-test-path-690"
} else {
"/zccache-nonexistent-test-path-690"
});
assert!(canonicalize_for_match(bogus).is_none());
}
}