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: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[resolver]
incompatible-rust-versions = "allow"
64 changes: 31 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ env:
ANDROID_VERSION: "12.0.0"
WASMTIME_BACKTRACE_DETAILS: 1

# Because Windows on GitHub CI
defaults:
run:
shell: bash

permissions: {}

name: CI
Expand All @@ -33,14 +38,32 @@ jobs:
- uses: EmbarkStudios/cargo-deny-action@91bf2b620e09e18d6eb78b92e7861937469acedb # v2
timeout-minutes: 10

determine_msrv:
name: Determining MSRV
runs-on: ubuntu-latest
outputs:
msrv: ${{ steps.msrv.outputs.MSRV }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Determine MSRV
id: msrv
run: |
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[] | select(.name == "tempfile") | "MSRV=\(.rust_version)"' \
>> "$GITHUB_OUTPUT"

build_and_test:
needs: determine_msrv
strategy:
fail-fast: false
matrix:
rust-version:
- nightly
- stable
- "1.63"
- "${{ needs.determine_msrv.outputs.msrv }}"
platform:
- name: "Linux"
os: ubuntu-latest
Expand All @@ -62,24 +85,6 @@ jobs:
os: ubuntu-latest
target: wasm32-wasip2
test-flags: --tests
exclude:
- rust-version: "1.63"
platform:
name: "Android"
os: ubuntu-latest
target: x86_64-linux-android
- rust-version: "1.63"
platform:
name: "WASI P1"
os: ubuntu-latest
target: wasm32-wasip1
test-flags: --tests
- rust-version: "1.63"
platform:
name: "WASI P2"
os: ubuntu-latest
target: wasm32-wasip2
test-flags: --tests
name: Platform Test (${{ matrix.platform.name }} / ${{ matrix.rust-version }})
runs-on: ${{ matrix.platform.os }}
steps:
Expand All @@ -92,20 +97,13 @@ jobs:
with:
toolchain: ${{ matrix.rust-version }}
targets: ${{ matrix.platform.target }}
- name: Generating the Cargo.lock
run: cargo generate-lockfile
- name: Downgrading once_cell
if: matrix.rust-version == 1.63
run: cargo update -p once_cell --precise 1.20.3
- name: Downgrading windows-sys
if: matrix.rust-version == 1.63
run: cargo update -p windows-sys --precise 0.60.2
- name: Downgrading getrandom
if: matrix.rust-version == 1.63
run: cargo update -p getrandom --precise 0.3.4
- name: Downgrading libc
if: matrix.rust-version == 1.63
run: cargo update -p libc --precise 0.2.183
- name: Selecting dependency versions
env:
CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS: 'fallback'
run: |
RUSTC_VERSION="$(rustc --version | sed 's/^rustc \([^ -]*\).*/\1/')"
sed -i -e 's/^rust-version = .*/rust-version = "'"$RUSTC_VERSION"'"/' Cargo.toml
cargo generate-lockfile
- name: Install Wasmtime
if: ${{ startsWith(matrix.platform.target, 'wasm32-wasi') }}
run: curl https://wasmtime.dev/install.sh -sSf | bash
Expand Down
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ authors = [
"Jason White <me@jasonwhite.io>",
]
documentation = "https://docs.rs/tempfile"
edition = "2021"
rust-version = "1.63"
edition = "2024"
rust-version = "1.85"
homepage = "https://stebalien.com/projects/tempfile-rs/"
keywords = ["tempfile", "tmpfile", "filesystem"]
license = "MIT OR Apache-2.0"
Expand All @@ -20,8 +20,6 @@ include = ["CHANGELOG.md", "Cargo.toml", "LICENSE-*", "README.md", "src/**/*.rs"

[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, <0.5", default-features = false, optional = true }
Expand All @@ -41,7 +39,6 @@ doc-comment = "0.3"

[features]
default = ["getrandom"]
nightly = [] # DEPRECATED

[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ patterns and surprisingly difficult to implement securely).

## Usage

Minimum required Rust version: 1.63.0
Minimum required Rust version: 1.85.0

Add this to your `Cargo.toml`:

Expand Down
2 changes: 1 addition & 1 deletion src/dir/imp/any.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::IoResultExt;
use crate::TempDir;
use crate::error::IoResultExt;
use std::path::PathBuf;
use std::{fs, io};

Expand Down
6 changes: 2 additions & 4 deletions src/dir/imp/unix.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::IoResultExt;
use crate::TempDir;
use crate::error::IoResultExt;
use std::io;
use std::path::PathBuf;

Expand All @@ -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
14 changes: 5 additions & 9 deletions src/dir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use std::mem;
use std::path::{self, Path, PathBuf};
use std::{fmt, io};

use crate::error::IoResultExt;
use crate::Builder;
use crate::error::IoResultExt;

#[cfg(doc)]
use crate::env;
Expand Down Expand Up @@ -380,13 +380,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 @@ -481,7 +474,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
84 changes: 60 additions & 24 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;
use crate::{Builder, tempdir_in, tempfile_in};

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 overridden 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())
}
4 changes: 2 additions & 2 deletions src/file/imp/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,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 Expand Up @@ -108,7 +108,7 @@ pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<
target_os = "redox",
))]
{
use rustix::fs::{renameat_with, RenameFlags, CWD};
use rustix::fs::{CWD, RenameFlags, renameat_with};
use rustix::io::Errno;
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};

Expand Down
8 changes: 4 additions & 4 deletions src/file/imp/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use std::{io, iter};

use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::{
MoveFileExW, ReOpenFile, SetFileAttributesW, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY,
FILE_FLAG_DELETE_ON_CLOSE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE,
FILE_SHARE_READ, FILE_SHARE_WRITE, MOVEFILE_REPLACE_EXISTING,
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY, FILE_FLAG_DELETE_ON_CLOSE, FILE_GENERIC_READ,
FILE_GENERIC_WRITE, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
MOVEFILE_REPLACE_EXISTING, MoveFileExW, ReOpenFile, SetFileAttributesW,
};

use crate::util;
Expand Down 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
Loading