Skip to main content

nxd_core/workspace/
local.rs

1use 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}