nxd_core/workspace/
local.rs1use crate::config;
2use std::fs;
3use std::path::PathBuf;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6pub fn sanitize_component(input: &str) -> String {
7 input
8 .chars()
9 .map(|ch| match ch {
10 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' => ch,
11 _ => '-',
12 })
13 .collect()
14}
15
16pub fn unique_suffix() -> String {
17 use std::sync::atomic::{AtomicUsize, Ordering};
18 static COUNTER: AtomicUsize = AtomicUsize::new(0);
19 let pid = std::process::id();
20 let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().subsec_nanos();
21 let count = COUNTER.fetch_add(1, Ordering::SeqCst);
22 format!("{}-{}-{}", pid, nanos, count)
23}
24
25pub fn clean_stale_temp_dirs() {
26 let mut dirs_to_clean = vec![PathBuf::from("/tmp")];
27 if let Ok(tmp_val) = std::env::var("TMPDIR") {
28 dirs_to_clean.push(PathBuf::from(tmp_val));
29 }
30 dirs_to_clean.dedup();
31
32 let app_prefix = format!("{}-", config::APP_NAME);
33 clean_owned_temp_dirs(&dirs_to_clean, &app_prefix);
34}
35
36fn clean_owned_temp_dirs(dirs_to_clean: &[PathBuf], app_prefix: &str) {
37 for dir in dirs_to_clean {
38 if let Ok(entries) = fs::read_dir(dir) {
39 for entry in entries.flatten() {
40 let name = entry.file_name().to_string_lossy().into_owned();
41 if name.starts_with(app_prefix) {
42 let full_path = entry.path();
43 if full_path.is_dir()
44 && fs::read_to_string(full_path.join(".nxd-owned")).ok().as_deref()
45 == Some("nxd-temp-v1\n")
46 {
47 let _ = fs::remove_dir_all(&full_path);
48 }
49 }
50 }
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_sanitize_component() {
61 assert_eq!(sanitize_component("my-host"), "my-host");
62 assert_eq!(sanitize_component("my host?"), "my-host-");
63 assert_eq!(sanitize_component("abc_123-DEF"), "abc_123-DEF");
64 }
65
66 #[test]
67 fn test_unique_suffix_non_empty() {
68 let s = unique_suffix();
69 assert!(!s.is_empty());
70 assert_ne!(unique_suffix(), s);
71 }
72
73 #[test]
74 fn stale_cleanup_removes_only_owned_directories() {
75 let root = std::env::temp_dir().join(format!("cleanup-scope-{}", unique_suffix()));
76 let owned = root.join("nxd-owned");
77 let unowned = root.join("nxd-unowned");
78 fs::create_dir_all(&owned).unwrap();
79 fs::create_dir_all(&unowned).unwrap();
80 fs::write(owned.join(".nxd-owned"), "nxd-temp-v1\n").unwrap();
81
82 clean_owned_temp_dirs(std::slice::from_ref(&root), "nxd-");
83
84 assert!(!owned.exists());
85 assert!(unowned.exists());
86 fs::remove_dir_all(root).unwrap();
87 }
88}