winbrew_core/
temp_workspace.rs

1//! Temporary workspace helpers for download and install flows.
2//!
3//! The temp-workspace contract keeps installer staging under a repo-independent
4//! directory derived from the OS temp root. That lets the download and install
5//! pipelines create disposable workspaces without polluting the managed root.
6
7use std::path::{Path, PathBuf};
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10static TEMP_ROOT_SUFFIX: AtomicUsize = AtomicUsize::new(0);
11
12/// Build a unique temp root for a package name and version.
13pub fn build_temp_root(name: &str, version: &str) -> PathBuf {
14    temp_root_base().join(temp_root_name(name, version))
15}
16
17/// Return the stable prefix used for temp root detection.
18pub fn temp_root_prefix(name: &str, version: &str) -> String {
19    format!(
20        "winbrew-install-{}-{}-",
21        sanitize_component(name),
22        sanitize_component(version)
23    )
24}
25
26/// Return the base temp directory used by WinBrew staging workspaces.
27pub fn temp_root_base() -> PathBuf {
28    std::env::temp_dir().join("winbrew")
29}
30
31/// Return `true` when a path belongs to the expected package temp root.
32pub fn is_temp_root_for(name: &str, version: &str, path: &Path) -> bool {
33    path.file_name()
34        .and_then(|value| value.to_str())
35        .map(|file_name| file_name.starts_with(&temp_root_prefix(name, version)))
36        .unwrap_or(false)
37}
38
39fn temp_root_name(name: &str, version: &str) -> String {
40    let suffix = TEMP_ROOT_SUFFIX.fetch_add(1, Ordering::Relaxed);
41    let mut segment = String::with_capacity(temp_root_prefix(name, version).len() + 20);
42    segment.push_str(&temp_root_prefix(name, version));
43    segment.push_str(&std::process::id().to_string());
44    segment.push('-');
45    segment.push_str(&suffix.to_string());
46
47    segment
48}
49
50fn sanitize_component(value: &str) -> String {
51    value
52        .chars()
53        .map(|ch| {
54            if ch.is_ascii_alphanumeric() || matches!(ch, '.' | '-' | '_') {
55                ch
56            } else {
57                '_'
58            }
59        })
60        .collect()
61}