Skip to content

Commit

Permalink
Merge pull request #177 from jasonwhite/jw/generic
Browse files Browse the repository at this point in the history
Add NamedTempFile<F>, Builder::{make,make_in}, and NamedTempFile::from_parts
  • Loading branch information
Stebalien authored Apr 10, 2022
2 parents 1a40687 + c040bef commit 66aa57f
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 61 deletions.
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

0 comments on commit 66aa57f

Please sign in to comment.