aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2023-02-23 12:59:05 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-02-23 12:59:05 +0000
commit988df0ca05e0177775e2b1ed568bf7b72c88db9b (patch)
tree632a499f62c12dc7f097c0214344371ef9d8384c
parentd0175ee513d52b39f1401c9152a2f368f57fe4b3 (diff)
parent4578b5b3b34d2dc74034747ceda1ebffa9c9acc5 (diff)
downloadtempfile-988df0ca05e0177775e2b1ed568bf7b72c88db9b.tar.gz
Upgrade tempfile to 3.3.0 am: 4578b5b3b3
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/tempfile/+/2421166 Change-Id: If394573a4b3432141e826ed2da80cdcc1bf1c593 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--.github/workflows/ci.yml52
-rw-r--r--.gitignore2
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml59
-rw-r--r--Cargo.toml.orig42
-rw-r--r--METADATA15
-rw-r--r--NEWS15
-rw-r--r--src/dir.rs31
-rw-r--r--src/file/imp/mod.rs2
-rw-r--r--src/file/imp/unix.rs2
-rw-r--r--src/file/mod.rs30
-rw-r--r--src/lib.rs45
-rw-r--r--src/spooled.rs42
-rw-r--r--src/util.rs29
-rw-r--r--tests/namedtempfile.rs49
16 files changed, 317 insertions, 108 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..cabd9cd
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "1a40687e06eb656044e3d2dffa1379f04b3ef3fd"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..0d7c068
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,52 @@
+on: [push]
+name: CI
+jobs:
+ build_and_test:
+ name: OS Test
+ strategy:
+ matrix:
+ rust-version:
+ - nightly
+ - "1.40"
+ os:
+ - ubuntu-latest
+ - windows-latest
+ - macos-latest
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Install Rust
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: ${{ matrix.rust-version }}
+ default: true
+ - name: Build
+ uses: actions-rs/cargo@v1
+ with:
+ command: build
+ - name: Test
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ wasm:
+ name: WASM Test Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Install Rust
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: nightly
+ default: true
+ - name: Install Cargo WASI
+ uses: actions-rs/cargo@v1
+ with:
+ command: install
+ args: cargo-wasi
+ - name: Build
+ uses: actions-rs/cargo@v1
+ with:
+ command: wasi
+ args: build --features nightly
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a9d37c5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target
+Cargo.lock
diff --git a/Android.bp b/Android.bp
index 67fafd2..9b54b9a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,13 +42,13 @@ rust_library {
host_supported: true,
crate_name: "tempfile",
cargo_env_compat: true,
- cargo_pkg_version: "3.2.0",
+ cargo_pkg_version: "3.3.0",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
"libcfg_if",
+ "libfastrand",
"liblibc",
- "librand",
"libremove_dir_all",
],
apex_available: [
diff --git a/Cargo.toml b/Cargo.toml
index 1f8e784..253f866 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,42 +1,43 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
[package]
-name = "tempfile"
-version = "3.2.0"
-authors = [
- "Steven Allen <steven@stebalien.com>",
- "The Rust Project Developers",
- "Ashley Mannix <ashleymannix@live.com.au>",
- "Jason White <jasonaw0@gmail.com>",
-]
-documentation = "https://docs.rs/tempfile"
edition = "2018"
+name = "tempfile"
+version = "3.3.0"
+authors = ["Steven Allen <steven@stebalien.com>", "The Rust Project Developers", "Ashley Mannix <ashleymannix@live.com.au>", "Jason White <jasonaw0@gmail.com>"]
exclude = ["/.travis.yml", "/appveyor.yml"]
+description = "A library for managing temporary files and directories."
homepage = "http://stebalien.com/projects/tempfile-rs"
+documentation = "https://docs.rs/tempfile"
keywords = ["tempfile", "tmpfile", "filesystem"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/Stebalien/tempfile"
-description = "A library for managing temporary files and directories."
-
-[dependencies]
-cfg-if = "1"
-rand = { version = "0.8", features = ["small_rng", "getrandom"], default_features = false }
-remove_dir_all = "0.5"
+[dependencies.cfg-if]
+version = "1"
-[target.'cfg(any(unix, target_os = "wasi"))'.dependencies]
-libc = "0.2.27"
+[dependencies.fastrand]
+version = "1.6.0"
-[target.'cfg(windows)'.dependencies.winapi]
+[dependencies.remove_dir_all]
+version = "0.5"
+[dev-dependencies.doc-comment]
version = "0.3"
-features = [
- "fileapi",
- "handleapi",
- "winbase",
-]
-
-[target.'cfg(target_os = "redox")'.dependencies]
-redox_syscall = "0.2"
-
-[dev-dependencies]
-doc-comment = "0.3"
[features]
nightly = []
+[target."cfg(any(unix, target_os = \"wasi\"))".dependencies.libc]
+version = "0.2.27"
+[target."cfg(target_os = \"redox\")".dependencies.redox_syscall]
+version = "0.2.9"
+[target."cfg(windows)".dependencies.winapi]
+version = "0.3"
+features = ["fileapi", "handleapi", "winbase"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..c5729c8
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,42 @@
+[package]
+name = "tempfile"
+version = "3.3.0"
+authors = [
+ "Steven Allen <steven@stebalien.com>",
+ "The Rust Project Developers",
+ "Ashley Mannix <ashleymannix@live.com.au>",
+ "Jason White <jasonaw0@gmail.com>",
+]
+documentation = "https://docs.rs/tempfile"
+edition = "2018"
+exclude = ["/.travis.yml", "/appveyor.yml"]
+homepage = "http://stebalien.com/projects/tempfile-rs"
+keywords = ["tempfile", "tmpfile", "filesystem"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/Stebalien/tempfile"
+description = "A library for managing temporary files and directories."
+
+[dependencies]
+cfg-if = "1"
+fastrand = "1.6.0"
+remove_dir_all = "0.5"
+
+[target.'cfg(any(unix, target_os = "wasi"))'.dependencies]
+libc = "0.2.27"
+
+[target.'cfg(windows)'.dependencies.winapi]
+version = "0.3"
+features = [
+ "fileapi",
+ "handleapi",
+ "winbase",
+]
+
+[target.'cfg(target_os = "redox")'.dependencies]
+redox_syscall = "0.2.9"
+
+[dev-dependencies]
+doc-comment = "0.3"
+
+[features]
+nightly = []
diff --git a/METADATA b/METADATA
index fee7cae..92ec9f7 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/tempfile
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "tempfile"
description: "A library for managing temporary files and directories."
third_party {
@@ -7,14 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/tempfile/tempfile-3.2.0.crate"
+ value: "https://static.crates.io/crates/tempfile/tempfile-3.3.0.crate"
}
- version: "3.2.0"
- # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+ version: "3.3.0"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 10
- day: 25
+ year: 2023
+ month: 2
+ day: 6
}
}
diff --git a/NEWS b/NEWS
index 6c83656..c284424 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,18 @@
+3.3.0
+=====
+
+Features:
+
+* Replace rand with fastrand for a significantly smaller dependency tree. Cryptographic randomness
+ isn't necessary for temporary file names, and isn't all that helpful either.
+* Add limited WASI support.
+* Add a function to extract the inner data from a `SpooledTempFile`.
+
+Bug Fixes:
+
+* Make it possible to persist unnamed temporary files on linux by removing the `O_EXCL` flag.
+* Fix redox minimum crate version.
+
3.2.0
=====
diff --git a/src/dir.rs b/src/dir.rs
index d630762..d5a944b 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -9,6 +9,7 @@
// except according to those terms.
use remove_dir_all::remove_dir_all;
+use std::mem;
use std::path::{self, Path, PathBuf};
use std::{fmt, fs, io};
@@ -192,7 +193,7 @@ pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct TempDir {
- path: Option<PathBuf>,
+ path: Box<Path>,
}
impl TempDir {
@@ -292,7 +293,7 @@ impl TempDir {
/// # }
/// ```
pub fn path(&self) -> &path::Path {
- self.path.as_ref().unwrap()
+ self.path.as_ref()
}
/// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
@@ -322,8 +323,13 @@ impl TempDir {
/// # Ok(())
/// # }
/// ```
- pub fn into_path(mut self) -> PathBuf {
- self.path.take().unwrap()
+ pub fn into_path(self) -> PathBuf {
+ // Prevent the Drop impl from being called.
+ let mut this = mem::ManuallyDrop::new(self);
+
+ // replace this.path with an empty Box, since an empty Box does not
+ // allocate any heap memory.
+ mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
}
/// Closes and removes the temporary directory, returning a `Result`.
@@ -369,8 +375,12 @@ impl TempDir {
pub fn close(mut self) -> io::Result<()> {
let result = remove_dir_all(self.path()).with_err_path(|| self.path());
- // Prevent the Drop impl from removing the dir a second time.
- self.path = None;
+ // Set self.path to empty Box to release the memory, since an empty
+ // Box does not allocate any heap memory.
+ self.path = PathBuf::new().into_boxed_path();
+
+ // Prevent the Drop impl from being called.
+ mem::forget(self);
result
}
@@ -392,15 +402,14 @@ impl fmt::Debug for TempDir {
impl Drop for TempDir {
fn drop(&mut self) {
- // Path is `None` if `close()` or `into_path()` has been called.
- if let Some(ref p) = self.path {
- let _ = remove_dir_all(p);
- }
+ let _ = remove_dir_all(self.path());
}
}
pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
fs::create_dir(&path)
.with_err_path(|| &path)
- .map(|_| TempDir { path: Some(path) })
+ .map(|_| TempDir {
+ path: path.into_boxed_path(),
+ })
}
diff --git a/src/file/imp/mod.rs b/src/file/imp/mod.rs
index ceb630e..fbb2bbf 100644
--- a/src/file/imp/mod.rs
+++ b/src/file/imp/mod.rs
@@ -1,4 +1,4 @@
-cfg_if! {
+cfg_if::cfg_if! {
if #[cfg(any(unix, target_os = "redox", target_os = "wasi"))] {
mod unix;
pub use self::unix::*;
diff --git a/src/file/imp/unix.rs b/src/file/imp/unix.rs
index 89249fd..480743c 100644
--- a/src/file/imp/unix.rs
+++ b/src/file/imp/unix.rs
@@ -2,7 +2,7 @@ use std::env;
use std::ffi::{CString, OsStr};
use std::fs::{self, File, OpenOptions};
use std::io;
-cfg_if! {
+cfg_if::cfg_if! {
if #[cfg(not(target_os = "wasi"))] {
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
diff --git a/src/file/mod.rs b/src/file/mod.rs
index 31fdd4b..b859ced 100644
--- a/src/file/mod.rs
+++ b/src/file/mod.rs
@@ -138,7 +138,7 @@ impl error::Error for PathPersistError {
///
/// When dropped, the temporary file is deleted.
pub struct TempPath {
- path: PathBuf,
+ path: Box<Path>,
}
impl TempPath {
@@ -176,8 +176,8 @@ impl TempPath {
/// # }
/// ```
pub fn close(mut self) -> io::Result<()> {
- let result = fs::remove_file(&self.path).with_err_path(|| &self.path);
- self.path = PathBuf::new();
+ let result = fs::remove_file(&self.path).with_err_path(|| &*self.path);
+ self.path = PathBuf::new().into_boxed_path();
mem::forget(self);
result
}
@@ -231,7 +231,7 @@ impl TempPath {
// Don't drop `self`. We don't want to try deleting the old
// temporary file path. (It'll fail, but the failure is never
// seen.)
- self.path = PathBuf::new();
+ self.path = PathBuf::new().into_boxed_path();
mem::forget(self);
Ok(())
}
@@ -293,7 +293,7 @@ impl TempPath {
// Don't drop `self`. We don't want to try deleting the old
// temporary file path. (It'll fail, but the failure is never
// seen.)
- self.path = PathBuf::new();
+ self.path = PathBuf::new().into_boxed_path();
mem::forget(self);
Ok(())
}
@@ -341,9 +341,9 @@ impl TempPath {
// Don't drop `self`. We don't want to try deleting the old
// temporary file path. (It'll fail, but the failure is never
// seen.)
- let path = mem::replace(&mut self.path, PathBuf::new());
+ let path = mem::replace(&mut self.path, PathBuf::new().into_boxed_path());
mem::forget(self);
- Ok(path)
+ Ok(path.into())
}
Err(e) => Err(PathPersistError {
error: e,
@@ -351,6 +351,18 @@ impl TempPath {
}),
}
}
+
+ /// Create a new TempPath from an existing path. This can be done even if no
+ /// file exists at the given path.
+ ///
+ /// This is mostly useful for interacting with libraries and external
+ /// components that provide files to be consumed or expect a path with no
+ /// existing file to be given.
+ pub fn from_path(path: impl Into<PathBuf>) -> Self {
+ Self {
+ path: path.into().into_boxed_path(),
+ }
+ }
}
impl fmt::Debug for TempPath {
@@ -953,7 +965,9 @@ pub(crate) fn create_named(
imp::create_named(&path, open_options)
.with_err_path(|| path.clone())
.map(|file| NamedTempFile {
- path: TempPath { path },
+ path: TempPath {
+ path: path.into_boxed_path(),
+ },
file,
})
}
diff --git a/src/lib.rs b/src/lib.rs
index d89798f..c38ca7b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,6 +27,42 @@
//! rely on file paths for _some_ operations. See the security documentation on
//! the `NamedTempFile` type for more information.
//!
+//! ## Early drop pitfall
+//!
+//! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead
+//! to an unexpected early removal of the directory/file, usually when working with APIs which are
+//! generic over `AsRef<Path>`. Consider the following example:
+//!
+//! ```no_run
+//! # use tempfile::tempdir;
+//! # use std::io;
+//! # use std::process::Command;
+//! # fn main() {
+//! # if let Err(_) = run() {
+//! # ::std::process::exit(1);
+//! # }
+//! # }
+//! # fn run() -> Result<(), io::Error> {
+//! // Create a directory inside of `std::env::temp_dir()`.
+//! let temp_dir = tempdir()?;
+//!
+//! // Spawn the `touch` command inside the temporary directory and collect the exit status
+//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference
+//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?;
+//! assert!(exit_status.success());
+//!
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the
+//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the
+//! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into
+//! an internal representation, with the original value being dropped and the directory thus
+//! being deleted, before the command can be executed.
+//!
+//! The `touch` command would fail with an `No such file or directory` error.
+//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
@@ -127,15 +163,8 @@
#![allow(clippy::redundant_field_names)]
#![cfg_attr(feature = "nightly", feature(wasi_ext))]
-#[macro_use]
-extern crate cfg_if;
-
-#[cfg(doctest)]
-#[macro_use]
-extern crate doc_comment;
-
#[cfg(doctest)]
-doctest!("../README.md");
+doc_comment::doctest!("../README.md");
const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;
diff --git a/src/spooled.rs b/src/spooled.rs
index e6f750e..ed6c16f 100644
--- a/src/spooled.rs
+++ b/src/spooled.rs
@@ -2,8 +2,9 @@ use crate::file::tempfile;
use std::fs::File;
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
+/// A wrapper for the two states of a `SpooledTempFile`.
#[derive(Debug)]
-enum SpooledInner {
+pub enum SpooledData {
InMemory(Cursor<Vec<u8>>),
OnDisk(File),
}
@@ -15,7 +16,7 @@ enum SpooledInner {
#[derive(Debug)]
pub struct SpooledTempFile {
max_size: usize,
- inner: SpooledInner,
+ inner: SpooledData,
}
/// Create a new spooled temporary file.
@@ -66,15 +67,15 @@ impl SpooledTempFile {
pub fn new(max_size: usize) -> SpooledTempFile {
SpooledTempFile {
max_size: max_size,
- inner: SpooledInner::InMemory(Cursor::new(Vec::new())),
+ inner: SpooledData::InMemory(Cursor::new(Vec::new())),
}
}
/// Returns true if the file has been rolled over to disk.
pub fn is_rolled(&self) -> bool {
match self.inner {
- SpooledInner::InMemory(_) => false,
- SpooledInner::OnDisk(_) => true,
+ SpooledData::InMemory(_) => false,
+ SpooledData::OnDisk(_) => true,
}
}
@@ -83,11 +84,11 @@ impl SpooledTempFile {
pub fn roll(&mut self) -> io::Result<()> {
if !self.is_rolled() {
let mut file = tempfile()?;
- if let SpooledInner::InMemory(ref mut cursor) = self.inner {
+ if let SpooledData::InMemory(ref mut cursor) = self.inner {
file.write_all(cursor.get_ref())?;
file.seek(SeekFrom::Start(cursor.position()))?;
}
- self.inner = SpooledInner::OnDisk(file);
+ self.inner = SpooledData::OnDisk(file);
}
Ok(())
}
@@ -97,20 +98,25 @@ impl SpooledTempFile {
self.roll()?; // does nothing if already rolled over
}
match self.inner {
- SpooledInner::InMemory(ref mut cursor) => {
+ SpooledData::InMemory(ref mut cursor) => {
cursor.get_mut().resize(size as usize, 0);
Ok(())
}
- SpooledInner::OnDisk(ref mut file) => file.set_len(size),
+ SpooledData::OnDisk(ref mut file) => file.set_len(size),
}
}
+
+ /// Consumes and returns the inner `SpooledData` type.
+ pub fn into_inner(self) -> SpooledData {
+ self.inner
+ }
}
impl Read for SpooledTempFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.inner {
- SpooledInner::InMemory(ref mut cursor) => cursor.read(buf),
- SpooledInner::OnDisk(ref mut file) => file.read(buf),
+ SpooledData::InMemory(ref mut cursor) => cursor.read(buf),
+ SpooledData::OnDisk(ref mut file) => file.read(buf),
}
}
}
@@ -119,7 +125,7 @@ impl Write for SpooledTempFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// roll over to file if necessary
let mut rolling = false;
- if let SpooledInner::InMemory(ref mut cursor) = self.inner {
+ if let SpooledData::InMemory(ref mut cursor) = self.inner {
rolling = cursor.position() as usize + buf.len() > self.max_size;
}
if rolling {
@@ -128,16 +134,16 @@ impl Write for SpooledTempFile {
// write the bytes
match self.inner {
- SpooledInner::InMemory(ref mut cursor) => cursor.write(buf),
- SpooledInner::OnDisk(ref mut file) => file.write(buf),
+ SpooledData::InMemory(ref mut cursor) => cursor.write(buf),
+ SpooledData::OnDisk(ref mut file) => file.write(buf),
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match self.inner {
- SpooledInner::InMemory(ref mut cursor) => cursor.flush(),
- SpooledInner::OnDisk(ref mut file) => file.flush(),
+ SpooledData::InMemory(ref mut cursor) => cursor.flush(),
+ SpooledData::OnDisk(ref mut file) => file.flush(),
}
}
}
@@ -145,8 +151,8 @@ impl Write for SpooledTempFile {
impl Seek for SpooledTempFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match self.inner {
- SpooledInner::InMemory(ref mut cursor) => cursor.seek(pos),
- SpooledInner::OnDisk(ref mut file) => file.seek(pos),
+ SpooledData::InMemory(ref mut cursor) => cursor.seek(pos),
+ SpooledData::OnDisk(ref mut file) => file.seek(pos),
}
}
}
diff --git a/src/util.rs b/src/util.rs
index 01beeed..8c91b9c 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,32 +1,17 @@
-use rand::{self, Rng, SeedableRng};
-use rand::{distributions::Alphanumeric, rngs::SmallRng};
+use fastrand;
use std::ffi::{OsStr, OsString};
-use std::thread_local;
-use std::{
- cell::UnsafeCell,
- path::{Path, PathBuf},
-};
-use std::{io, str};
+use std::path::{Path, PathBuf};
+use std::{io, iter::repeat_with};
use crate::error::IoResultExt;
-thread_local! {
- static THREAD_RNG: UnsafeCell<SmallRng> = UnsafeCell::new(SmallRng::from_entropy());
-}
-
fn tmpname(prefix: &OsStr, suffix: &OsStr, rand_len: usize) -> OsString {
let mut buf = OsString::with_capacity(prefix.len() + suffix.len() + rand_len);
buf.push(prefix);
-
- // Push each character in one-by-one. Unfortunately, this is the only
- // safe(ish) simple way to do this without allocating a temporary
- // String/Vec.
- THREAD_RNG.with(|rng| unsafe {
- (&mut *rng.get())
- .sample_iter(&Alphanumeric)
- .take(rand_len)
- .for_each(|b| buf.push(str::from_utf8_unchecked(&[b as u8])))
- });
+ let mut char_buf = [0u8; 4];
+ for c in repeat_with(fastrand::alphanumeric).take(rand_len) {
+ buf.push(c.encode_utf8(&mut char_buf));
+ }
buf.push(suffix);
buf
}
diff --git a/tests/namedtempfile.rs b/tests/namedtempfile.rs
index a489d56..d2c7da2 100644
--- a/tests/namedtempfile.rs
+++ b/tests/namedtempfile.rs
@@ -1,10 +1,11 @@
#![deny(rust_2018_idioms)]
use std::env;
+use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
-use std::path::Path;
-use tempfile::{Builder, NamedTempFile};
+use std::path::{Path, PathBuf};
+use tempfile::{tempdir, Builder, NamedTempFile, TempPath};
fn exists<P: AsRef<Path>>(path: P) -> bool {
std::fs::metadata(path.as_ref()).is_ok()
@@ -217,6 +218,50 @@ fn test_temppath_persist_noclobber() {
}
#[test]
+fn temp_path_from_existing() {
+ let tmp_dir = tempdir().unwrap();
+ let tmp_file_path_1 = tmp_dir.path().join("testfile1");
+ let tmp_file_path_2 = tmp_dir.path().join("testfile2");
+
+ File::create(&tmp_file_path_1).unwrap();
+ assert!(tmp_file_path_1.exists(), "Test file 1 hasn't been created");
+
+ File::create(&tmp_file_path_2).unwrap();
+ assert!(tmp_file_path_2.exists(), "Test file 2 hasn't been created");
+
+ let tmp_path = TempPath::from_path(&tmp_file_path_1);
+ assert!(
+ tmp_file_path_1.exists(),
+ "Test file has been deleted before dropping TempPath"
+ );
+
+ drop(tmp_path);
+ assert!(
+ !tmp_file_path_1.exists(),
+ "Test file exists after dropping TempPath"
+ );
+ assert!(
+ tmp_file_path_2.exists(),
+ "Test file 2 has been deleted before dropping TempDir"
+ );
+}
+
+#[test]
+#[allow(unreachable_code)]
+fn temp_path_from_argument_types() {
+ // This just has to compile
+ return;
+
+ TempPath::from_path("");
+ TempPath::from_path(String::new());
+ TempPath::from_path(OsStr::new(""));
+ TempPath::from_path(OsString::new());
+ TempPath::from_path(Path::new(""));
+ TempPath::from_path(PathBuf::new());
+ TempPath::from_path(PathBuf::new().into_boxed_path());
+}
+
+#[test]
fn test_write_after_close() {
let path = NamedTempFile::new().unwrap().into_temp_path();
File::create(path).unwrap().write_all(b"test").unwrap();