Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
rust-version:
- nightly
- stable
- "1.63"
- "1.84"
platform:
- name: "Linux"
os: ubuntu-latest
Expand Down
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = [
]
documentation = "https://docs.rs/tempfile"
edition = "2021"
rust-version = "1.63"
rust-version = "1.84"
homepage = "https://stebalien.com/projects/tempfile-rs/"
keywords = ["tempfile", "tmpfile", "filesystem"]
license = "MIT OR Apache-2.0"
Expand All @@ -18,8 +18,6 @@ description = "A library for managing temporary files and directories."

[dependencies]
fastrand = "2.1.1"
# Not available in stdlib until 1.70, but we support 1.63 to support Debian stable.
once_cell = { version = "1.19.0", default-features = false, features = ["std"] }

[target.'cfg(any(unix, windows, target_os = "wasi"))'.dependencies]
getrandom = { version = "0.3.0", default-features = false, optional = true }
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ patterns and surprisingly difficult to implement securely).
Usage
-----

Minimum required Rust version: 1.63.0
Minimum required Rust version: 1.84.0

Add this to your `Cargo.toml`:

Expand Down
4 changes: 1 addition & 3 deletions src/dir/imp/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ pub fn create(
#[cfg(not(target_os = "wasi"))]
{
use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
if let Some(p) = permissions {
dir_options.mode(p.mode());
}
dir_options.mode(permissions.map(|p| p.mode()).unwrap_or(0o700));
}
dir_options
.create(&path)
Expand Down
12 changes: 4 additions & 8 deletions src/dir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,6 @@ impl TempDir {
self.path.as_ref()
}

/// Deprecated alias for [`TempDir::keep`].
#[must_use]
#[deprecated = "use TempDir::keep()"]
pub fn into_path(self) -> PathBuf {
self.keep()
}

/// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
///
/// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
Expand Down Expand Up @@ -483,7 +476,10 @@ impl TempDir {
}
}

impl AsRef<Path> for TempDir {
// NOTE: This is implemented on &TempDir, not TempDir, to prevent accidentally moving the TempDir
// into a function that calls `as_ref()` before immediately dropping it (deleting the underlying
// temporary directory).
impl AsRef<Path> for &TempDir {
fn as_ref(&self) -> &Path {
self.path()
}
Expand Down
82 changes: 59 additions & 23 deletions src/env.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::sync::{LazyLock, OnceLock};
use std::{env, io};

#[cfg(doc)]
use crate::{tempdir_in, tempfile_in, Builder};

// Once rust 1.70 is wide-spread (Debian stable), we can use OnceLock from stdlib.
use once_cell::sync::OnceCell as OnceLock;

static ENV_TEMPDIR: LazyLock<Option<PathBuf>> =
// Only call env::temp_dir() on platforms known to not panic.
LazyLock::new(if cfg!(any(unix, windows, target_os = "hermit")) {
|| Some(env::temp_dir())
} else {
|| None
});
static DEFAULT_TEMPDIR: OnceLock<PathBuf> = OnceLock::new();
static DEFAULT_PREFIX: OnceLock<OsString> = OnceLock::new();

/// Override the default temporary directory (defaults to [`std::env::temp_dir`]). This function
/// changes the _global_ default temporary directory for the entire program and should not be called
Expand All @@ -18,34 +25,63 @@ static DEFAULT_TEMPDIR: OnceLock<PathBuf> = OnceLock::new();
/// should instead use the `_in` variants of the various temporary file/directory constructors
/// ([`tempdir_in`], [`tempfile_in`], the so-named functions on [`Builder`], etc.).
///
/// Only the first call to this function will succeed. All further calls will fail with `Err(path)`
/// where `path` is previously set default temporary directory override.
/// Only the **first** call to this function will succeed and return `Ok(path)` where `path` is a
/// static reference to the temporary directory. All further calls will fail with `Err(path)` where
/// `path` is the previously set default temporary directory override.
///
/// **NOTE:** This function does not check if the specified directory exists and/or is writable.
pub fn override_temp_dir(path: &Path) -> Result<(), PathBuf> {
let mut we_set = false;
let val = DEFAULT_TEMPDIR.get_or_init(|| {
we_set = true;
path.to_path_buf()
});
if we_set {
Ok(())
} else {
Err(val.to_owned())
pub fn override_temp_dir(path: impl Into<PathBuf>) -> Result<&'static Path, &'static Path> {
let mut path = Some(path.into());
let val = DEFAULT_TEMPDIR.get_or_init(|| path.take().unwrap());
match path {
Some(_) => Err(val),
None => Ok(val),
}
}

/// Returns the default temporary directory, used for both temporary directories and files if no
/// directory is explicitly specified.
///
/// This function simply delegates to [`std::env::temp_dir`] unless the default temporary directory
/// has been override by a call to [`override_temp_dir`].
/// Unless the default temporary directory has been overridden by a call to [`override_temp_dir`],
/// this function delegates to [`std::env::temp_dir`] (when supported by the platform). It returns
/// an error on platforms with no default temporary directory (e.g., WASI/WASM) unless
/// [`override_temp_dir`] has already been called to set the temporary directory.
///
/// **NOTE:**
///
/// **NOTE:** This function does not check if the returned directory exists and/or is writable.
pub fn temp_dir() -> PathBuf {
/// 1. This function does not check if the returned directory exists and/or is writable.
/// 2. This function caches the result of [`std::env::temp_dir`]. Any future changes to, e.g., the
/// `TMPDIR` environment variable won't have any effect.
pub fn temp_dir() -> io::Result<&'static Path> {
DEFAULT_TEMPDIR
.get()
.map(|p| p.to_owned())
// Don't cache this in case the user uses std::env::set to change the temporary directory.
.unwrap_or_else(env::temp_dir)
.or_else(|| ENV_TEMPDIR.as_ref())
.map(|a| &**a)
.ok_or_else(|| io::Error::other("no temporary directory configured"))
}

/// Override the default prefix for new temporary files (defaults to "tmp"). This function changes
/// the _global_ default prefix used by the entire program and should only be used by the top-level
/// application. It's recommended that the top-level application call this function to specify an
/// application-specific prefix to make it easier to identify temporary files belonging to the
/// application.
///
/// Only the **first** call to this function will succeed and return `Ok(prefix)` where `prefix` is
/// a static reference to the default temporary file prefix. All further calls will fail with
/// `Err(prefix)` where `prefix` is the previously set default temporary file prefix.
pub fn override_default_prefix(
prefix: impl Into<OsString>,
) -> Result<&'static OsStr, &'static OsStr> {
let mut prefix = Some(prefix.into());
let val = DEFAULT_PREFIX.get_or_init(|| prefix.take().unwrap());
match prefix {
Some(_) => Err(val),
None => Ok(val),
}
}

/// Returns the default prefix used for new temporary files if no prefix is explicitly specified via
/// [`Builder::prefix`].
pub fn default_prefix() -> &'static OsStr {
DEFAULT_PREFIX.get().map(|p| &**p).unwrap_or("tmp".as_ref())
}
2 changes: 1 addition & 1 deletion src/file/imp/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ pub fn create(dir: &Path) -> io::Result<File> {
fn create_unix(dir: &Path) -> io::Result<File> {
util::create_helper(
dir,
OsStr::new(".tmp"),
crate::env::default_prefix(),
OsStr::new(""),
crate::NUM_RAND_CHARS,
|path| create_unlinked(&path),
Expand Down
2 changes: 1 addition & 1 deletion src/file/imp/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn create_named(
pub fn create(dir: &Path) -> io::Result<File> {
util::create_helper(
dir,
OsStr::new(".tmp"),
crate::env::default_prefix(),
OsStr::new(""),
crate::NUM_RAND_CHARS,
|path| {
Expand Down
15 changes: 11 additions & 4 deletions src/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod imp;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn tempfile() -> io::Result<File> {
tempfile_in(env::temp_dir())
tempfile_in(env::temp_dir()?)
}

/// Create a new temporary file in the specified directory. Also see [`tempfile`].
Expand Down Expand Up @@ -368,13 +368,13 @@ impl Deref for TempPath {
}
}

impl AsRef<Path> for TempPath {
impl AsRef<Path> for &TempPath {
fn as_ref(&self) -> &Path {
&self.path
}
}

impl AsRef<OsStr> for TempPath {
impl AsRef<OsStr> for &TempPath {
fn as_ref(&self) -> &OsStr {
self.path.as_os_str()
}
Expand Down Expand Up @@ -458,7 +458,7 @@ impl<F> fmt::Debug for NamedTempFile<F> {
}
}

impl<F> AsRef<Path> for NamedTempFile<F> {
impl<F> AsRef<Path> for &NamedTempFile<F> {
#[inline]
fn as_ref(&self) -> &Path {
self.path()
Expand Down Expand Up @@ -1021,12 +1021,19 @@ impl<F: Seek> Seek for NamedTempFile<F> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.as_file_mut().seek(pos).with_err_path(|| self.path())
}
fn rewind(&mut self) -> io::Result<()> {
self.as_file_mut().rewind().with_err_path(|| self.path())
}
}

impl Seek for &NamedTempFile<File> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.as_file().seek(pos).with_err_path(|| self.path())
}

fn rewind(&mut self) -> io::Result<()> {
self.as_file().rewind().with_err_path(|| self.path())
}
}

#[cfg(any(unix, target_os = "wasi"))]
Expand Down
Loading
Loading