1use std::path::{Path, PathBuf};
4
5use tempfile::{Builder, TempDir};
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum TempMode {
9 Normal,
11 KeepOnFailure,
13 ShmDebug,
16}
17
18impl TempMode {
19 fn base_dir(&self) -> PathBuf {
20 let shm = Path::new("/dev/shm");
21 match self {
22 Self::Normal | Self::ShmDebug => {
23 if shm.is_dir() {
24 shm.to_path_buf()
25 } else {
26 std::env::temp_dir()
27 }
28 }
29 Self::KeepOnFailure => std::env::temp_dir(),
30 }
31 }
32}
33
34pub struct Workspace {
35 dir: TempDir,
36 mode: TempMode,
37}
38
39impl Workspace {
40 pub fn new(mode: TempMode) -> Result<Self, Box<dyn std::error::Error>> {
41 let dir = Builder::new().prefix("mw-").tempdir_in(mode.base_dir())?;
42 Ok(Self { dir, mode })
43 }
44
45 pub fn path(&self) -> &Path {
46 self.dir.path()
47 }
48
49 pub fn on_failure(self) {
53 if self.mode == TempMode::KeepOnFailure {
54 let path = self.dir.keep();
55 eprintln!("note: temp files retained at {}", path.display());
56 }
57 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_workspace_normal_creates_dir() {
67 let ws = Workspace::new(TempMode::Normal).unwrap();
68 assert!(ws.path().is_dir());
69 }
70
71 #[test]
72 fn test_workspace_cleans_up_on_drop() {
73 let path = {
74 let ws = Workspace::new(TempMode::Normal).unwrap();
75 ws.path().to_path_buf()
76 };
77 assert!(!path.exists(), "workspace dir should be deleted on drop");
78 }
79
80 #[test]
81 fn test_workspace_keep_on_failure_leaks() {
82 let path = {
83 let ws = Workspace::new(TempMode::KeepOnFailure).unwrap();
84 let p = ws.path().to_path_buf();
85 ws.on_failure();
86 p
87 };
88 assert!(path.exists(), "KeepOnFailure should retain the dir");
89 std::fs::remove_dir_all(&path).unwrap();
90 }
91
92 #[test]
93 fn test_workspace_shm_debug_cleans_up_on_failure() {
94 let path = {
95 let ws = Workspace::new(TempMode::ShmDebug).unwrap();
96 let p = ws.path().to_path_buf();
97 ws.on_failure();
98 p
99 };
100 assert!(!path.exists(), "ShmDebug should always clean up");
101 }
102}