1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
use std::path;
use super::errors::*;
/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new().unwrap();
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
///
/// [`File`]: std::fs::File
/// [`Path`]: std::path::Path
/// [`ReadDir`]: std::fs::ReadDir
pub struct TempDir {
temp: Inner,
}
enum Inner {
Temp(tempfile::TempDir),
Persisted(path::PathBuf),
}
impl TempDir {
/// Attempts to make a temporary directory inside of `env::temp_dir()`.
///
/// The directory and everything inside it will be automatically deleted
/// once the returned `TempDir` is destroyed.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new().unwrap();
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
pub fn new() -> Result<Self, FixtureError> {
let temp = tempfile::TempDir::new().chain(FixtureError::new(FixtureKind::CreateDir))?;
let temp = Inner::Temp(temp);
Ok(Self { temp })
}
/// Conditionally persist the temporary directory for debug purposes.
///
/// Note: this operation is not reversible, i.e. `into_persistent_if(false)` is a no-op.
///
/// # Examples
///
/// ```no_run
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new()
/// .unwrap()
/// .into_persistent_if(std::env::var_os("TEST_PERSIST_FILES").is_some());
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
pub fn into_persistent_if(self, yes: bool) -> Self {
if !yes {
return self;
}
self.into_persistent()
}
/// Persist the temporary directory for debug purposes.
///
/// Note: this operation is not reversible, i.e. `into_persistent_if(false)` is a no-op.
///
/// # Examples
///
/// ```no_run
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new()
/// .unwrap()
/// .into_persistent();
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
pub fn into_persistent(self) -> Self {
let path = match self.temp {
Inner::Temp(temp) => temp.into_path(),
Inner::Persisted(path) => path,
};
let temp = Inner::Persisted(path);
Self { temp }
}
/// Accesses the [`Path`] to the temporary directory.
///
/// [`Path`]: std::path::Path
///
/// # Examples
///
/// ```
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new().unwrap();
///
/// println!("{}", tmp_dir.path().display());
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
pub fn path(&self) -> &path::Path {
match self.temp {
Inner::Temp(ref temp) => temp.path(),
Inner::Persisted(ref path) => path.as_path(),
}
}
/// Closes and removes the temporary directory, returning a `Result`.
///
/// Although `TempDir` removes the directory on drop, in the destructor
/// any errors are ignored. To detect errors cleaning up the temporary
/// directory, call `close` instead.
///
/// # Errors
///
/// This function may return a variety of [`std::io::Error`]s that result from deleting
/// the files and directories contained with the temporary directory,
/// as well as from deleting the temporary directory itself. These errors
/// may be platform specific.
///
///
/// # Examples
///
/// ```
/// use assert_fs::fixture::TempDir;
///
/// let tmp_dir = TempDir::new().unwrap();
///
/// // Ensure deletion happens.
/// tmp_dir.close().unwrap();
/// ```
pub fn close(self) -> Result<(), FixtureError> {
match self.temp {
Inner::Temp(temp) => temp
.close()
.chain(FixtureError::new(FixtureKind::Cleanup))?,
Inner::Persisted(_) => (),
}
Ok(())
}
}
impl AsRef<path::Path> for TempDir {
fn as_ref(&self) -> &path::Path {
self.path()
}
}
impl std::ops::Deref for TempDir {
type Target = path::Path;
#[inline]
fn deref(&self) -> &path::Path {
self.path()
}
}