snapbox/dir/
root.rs

1/// Working directory for tests
2#[derive(Debug)]
3pub struct DirRoot(DirRootInner);
4
5#[derive(Debug)]
6enum DirRootInner {
7    None,
8    Immutable(std::path::PathBuf),
9    #[cfg(feature = "dir")]
10    MutablePath(std::path::PathBuf),
11    #[cfg(feature = "dir")]
12    MutableTemp {
13        temp: tempfile::TempDir,
14        path: std::path::PathBuf,
15    },
16}
17
18impl DirRoot {
19    pub fn none() -> Self {
20        Self(DirRootInner::None)
21    }
22
23    pub fn immutable(target: &std::path::Path) -> Self {
24        Self(DirRootInner::Immutable(target.to_owned()))
25    }
26
27    #[cfg(feature = "dir")]
28    pub fn mutable_temp() -> Result<Self, crate::assert::Error> {
29        let temp = tempfile::tempdir().map_err(|e| e.to_string())?;
30        // We need to get the `/private` prefix on Mac so variable substitutions work
31        // correctly
32        let path = crate::dir::canonicalize(temp.path())
33            .map_err(|e| format!("Failed to canonicalize {}: {}", temp.path().display(), e))?;
34        Ok(Self(DirRootInner::MutableTemp { temp, path }))
35    }
36
37    #[cfg(feature = "dir")]
38    pub fn mutable_at(target: &std::path::Path) -> Result<Self, crate::assert::Error> {
39        let _ = std::fs::remove_dir_all(target);
40        std::fs::create_dir_all(target)
41            .map_err(|e| format!("Failed to create {}: {}", target.display(), e))?;
42        Ok(Self(DirRootInner::MutablePath(target.to_owned())))
43    }
44
45    #[cfg(feature = "dir")]
46    pub fn with_template<F>(self, template: &F) -> Result<Self, crate::assert::Error>
47    where
48        F: crate::dir::DirFixture + ?Sized,
49    {
50        match &self.0 {
51            DirRootInner::None | DirRootInner::Immutable(_) => {
52                return Err("Sandboxing is disabled".into());
53            }
54            DirRootInner::MutablePath(path) | DirRootInner::MutableTemp { path, .. } => {
55                crate::debug!("Initializing {} from {:?}", path.display(), template);
56                template.write_to_path(path)?;
57            }
58        }
59
60        Ok(self)
61    }
62
63    pub fn is_mutable(&self) -> bool {
64        match &self.0 {
65            DirRootInner::None | DirRootInner::Immutable(_) => false,
66            #[cfg(feature = "dir")]
67            DirRootInner::MutablePath(_) => true,
68            #[cfg(feature = "dir")]
69            DirRootInner::MutableTemp { .. } => true,
70        }
71    }
72
73    pub fn path(&self) -> Option<&std::path::Path> {
74        match &self.0 {
75            DirRootInner::None => None,
76            DirRootInner::Immutable(path) => Some(path.as_path()),
77            #[cfg(feature = "dir")]
78            DirRootInner::MutablePath(path) => Some(path.as_path()),
79            #[cfg(feature = "dir")]
80            DirRootInner::MutableTemp { path, .. } => Some(path.as_path()),
81        }
82    }
83
84    /// Explicitly close to report errors
85    pub fn close(self) -> Result<(), std::io::Error> {
86        match self.0 {
87            DirRootInner::None | DirRootInner::Immutable(_) => Ok(()),
88            #[cfg(feature = "dir")]
89            DirRootInner::MutablePath(_) => Ok(()),
90            #[cfg(feature = "dir")]
91            DirRootInner::MutableTemp { temp, .. } => temp.close(),
92        }
93    }
94}
95
96impl Default for DirRoot {
97    fn default() -> Self {
98        Self::none()
99    }
100}