Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NamedTempFile<F>, Builder::{make,make_in}, and NamedTempFile::from_parts #177

Merged
merged 4 commits into from
Apr 10, 2022
Merged
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = [
"Steven Allen <steven@stebalien.com>",
"The Rust Project Developers",
"Ashley Mannix <ashleymannix@live.com.au>",
"Jason White <jasonaw0@gmail.com>",
"Jason White <me@jasonwhite.io>",
]
documentation = "https://docs.rs/tempfile"
edition = "2018"
Expand Down
10 changes: 10 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
3.4.0 (WIP)
=====

Features:

* Add `Builder::make` and `Builder::make_in` for generalized temp file
creation.
* Add `NamedTempFile::from_parts` to complement `NamedTempFile::into_parts`.
* Add generic parameter to `NamedTempFile` to support wrapping non-File types.

3.3.0
=====

Expand Down
148 changes: 90 additions & 58 deletions src/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,60 +478,65 @@ impl AsRef<OsStr> for TempPath {
/// [`NamedTempFile::new_in()`]: #method.new_in
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
pub struct NamedTempFile {
pub struct NamedTempFile<F = File> {
path: TempPath,
file: File,
file: F,
}

impl fmt::Debug for NamedTempFile {
impl<F> fmt::Debug for NamedTempFile<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NamedTempFile({:?})", self.path)
}
}

impl AsRef<Path> for NamedTempFile {
impl<F> AsRef<Path> for NamedTempFile<F> {
#[inline]
fn as_ref(&self) -> &Path {
self.path()
}
}

/// Error returned when persisting a temporary file fails.
#[derive(Debug)]
pub struct PersistError {
pub struct PersistError<F = File> {
/// The underlying IO error.
pub error: io::Error,
/// The temporary file that couldn't be persisted.
pub file: NamedTempFile,
pub file: NamedTempFile<F>,
}

impl<F> fmt::Debug for PersistError<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PersistError({:?})", self.error)
}
}

impl From<PersistError> for io::Error {
impl<F> From<PersistError<F>> for io::Error {
#[inline]
fn from(error: PersistError) -> io::Error {
fn from(error: PersistError<F>) -> io::Error {
error.error
}
}

impl From<PersistError> for NamedTempFile {
impl<F> From<PersistError<F>> for NamedTempFile<F> {
#[inline]
fn from(error: PersistError) -> NamedTempFile {
fn from(error: PersistError<F>) -> NamedTempFile<F> {
error.file
}
}

impl fmt::Display for PersistError {
impl<F> fmt::Display for PersistError<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to persist temporary file: {}", self.error)
}
}

impl error::Error for PersistError {
impl<F> error::Error for PersistError<F> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.error)
}
}

impl NamedTempFile {
impl NamedTempFile<File> {
/// Create a new named temporary file.
///
/// See [`Builder`] for more configuration.
Expand Down Expand Up @@ -601,7 +606,9 @@ impl NamedTempFile {
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> {
Builder::new().tempfile_in(dir)
}
}

impl<F> NamedTempFile<F> {
/// Get the temporary file's path.
///
/// # Security
Expand Down Expand Up @@ -711,7 +718,7 @@ impl NamedTempFile {
/// ```
///
/// [`PersistError`]: struct.PersistError.html
pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> {
pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<F, PersistError<F>> {
let NamedTempFile { path, file } = self;
match path.persist(new_path) {
Ok(_) => Ok(file),
Expand Down Expand Up @@ -764,7 +771,7 @@ impl NamedTempFile {
/// # Ok(())
/// # }
/// ```
pub fn persist_noclobber<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> {
pub fn persist_noclobber<P: AsRef<Path>>(self, new_path: P) -> Result<F, PersistError<F>> {
let NamedTempFile { path, file } = self;
match path.persist_noclobber(new_path) {
Ok(_) => Ok(file),
Expand Down Expand Up @@ -808,7 +815,7 @@ impl NamedTempFile {
/// ```
///
/// [`PathPersistError`]: struct.PathPersistError.html
pub fn keep(self) -> Result<(File, PathBuf), PersistError> {
pub fn keep(self) -> Result<(F, PathBuf), PersistError<F>> {
let (file, path) = (self.file, self.path);
match path.keep() {
Ok(path) => Ok((file, path)),
Expand All @@ -819,6 +826,49 @@ impl NamedTempFile {
}
}

/// Get a reference to the underlying file.
pub fn as_file(&self) -> &F {
&self.file
}

/// Get a mutable reference to the underlying file.
pub fn as_file_mut(&mut self) -> &mut F {
&mut self.file
}

/// Convert the temporary file into a `std::fs::File`.
///
/// The inner file will be deleted.
pub fn into_file(self) -> F {
self.file
}

/// Closes the file, leaving only the temporary file path.
///
/// This is useful when another process must be able to open the temporary
/// file.
pub fn into_temp_path(self) -> TempPath {
self.path
}

/// Converts the named temporary file into its constituent parts.
///
/// Note: When the path is dropped, the file is deleted but the file handle
/// is still usable.
pub fn into_parts(self) -> (F, TempPath) {
(self.file, self.path)
}

/// Creates a `NamedTempFile` from its constituent parts.
///
/// This can be used with [`NamedTempFile::into_parts`] to reconstruct the
/// `NamedTempFile`.
pub fn from_parts(file: F, path: TempPath) -> Self {
Self { file, path }
}
}

impl NamedTempFile<File> {
/// Securely reopen the temporary file.
///
/// This function is useful when you need multiple independent handles to
Expand Down Expand Up @@ -858,54 +908,24 @@ impl NamedTempFile {
imp::reopen(self.as_file(), NamedTempFile::path(self))
.with_err_path(|| NamedTempFile::path(self))
}

/// Get a reference to the underlying file.
pub fn as_file(&self) -> &File {
&self.file
}

/// Get a mutable reference to the underlying file.
pub fn as_file_mut(&mut self) -> &mut File {
&mut self.file
}

/// Convert the temporary file into a `std::fs::File`.
///
/// The inner file will be deleted.
pub fn into_file(self) -> File {
self.file
}

/// Closes the file, leaving only the temporary file path.
///
/// This is useful when another process must be able to open the temporary
/// file.
pub fn into_temp_path(self) -> TempPath {
self.path
}

/// Converts the named temporary file into its constituent parts.
///
/// Note: When the path is dropped, the file is deleted but the file handle
/// is still usable.
pub fn into_parts(self) -> (File, TempPath) {
(self.file, self.path)
}
}

impl Read for NamedTempFile {
impl<F: Read> Read for NamedTempFile<F> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.as_file_mut().read(buf).with_err_path(|| self.path())
}
}

impl<'a> Read for &'a NamedTempFile {
impl<'a, F> Read for &'a NamedTempFile<F>
where
&'a F: Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.as_file().read(buf).with_err_path(|| self.path())
}
}

impl Write for NamedTempFile {
impl<F: Write> Write for NamedTempFile<F> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.as_file_mut().write(buf).with_err_path(|| self.path())
}
Expand All @@ -915,7 +935,10 @@ impl Write for NamedTempFile {
}
}

impl<'a> Write for &'a NamedTempFile {
impl<'a, F> Write for &'a NamedTempFile<F>
where
&'a F: Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.as_file().write(buf).with_err_path(|| self.path())
}
Expand All @@ -925,28 +948,37 @@ impl<'a> Write for &'a NamedTempFile {
}
}

impl Seek for NamedTempFile {
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())
}
}

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

#[cfg(unix)]
impl std::os::unix::io::AsRawFd for NamedTempFile {
impl<F> std::os::unix::io::AsRawFd for NamedTempFile<F>
where
F: std::os::unix::io::AsRawFd,
{
#[inline]
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.as_file().as_raw_fd()
}
}

#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for NamedTempFile {
impl<F> std::os::windows::io::AsRawHandle for NamedTempFile<F>
where
F: std::os::windows::io::AsRawHandle,
{
#[inline]
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.as_file().as_raw_handle()
Expand Down
Loading