rust-lang / rust-lang/libs-team

Add basic tempfile API to stdlib

Open
#420 13 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

api-change-proposal
Dominant language
Rust
Stars
178
Forks
28
Avg merge
15m
Merged PRs (30d)
1

Description

Proposal

Problem statement

Tempfile is a widely used functionality, with the previous breaking change of the tempfile crate being about 7 years ago, I believe its API is stable enough that it should be part of the stdlib.

Motivating examples or use cases

The crate tempfile has 168,118,646 downloads and is used very often in the ecosystem, I believe it is quite a fundamental functionality that the ecosystem depends on.

Having it in stdlib would make it

  • easier to access
  • more trusted than third-party dependencies, one less crate to audit on
  • faster to compile for low-level crates depending on it, which might block compilation of other crates

Solution sketch

mod fs {
    impl File {
        /// Create a new temporary file.
        /// The new file could be in memory or on disk, there's no guarantee about it.
        ///
        /// The temporary file will be automatically removed by the OS when the last handle to it is closed.
        /// This doesn’t rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file.
        ///
        /// # Security
        /// NOTE that other processes might be able to get access to your tempfile (via ptrace, procfs, kernel bug, etc).
        /// It does not provide any security guarantee on that.
        pub fn tempfile() -> Result<File>;

        /// Create a new temporary file under the path `dir`, the file will be created on the filesystem
        /// `dir` is in.
        ///
        /// # Security
        /// NOTE that other processes might be able to get access to your tempfile (via ptrace, procfs, kernel bug, etc).
        /// It does not provide any security guarantee on that.
        pub fn tempfile_in<P: AsRef<Path>>(dir: P) -> Result<File>;

        /// Create a hardlink to `path`.
        /// If `self` is a tempfile, then it will be visible at `path`.
        ///
        /// NOTE that if `self` is a tempfile, then this operation can fail with `io::ErrorKind::Unsupported`.
        pub fn hardlink<P: AsRef<Path>>(&self, path: P) -> Result<()>;
    }

    impl OpenOptions {
        /// Open a tempfile under `dir`, the `File` returned will be writable even if `.write(true)` is not called.
        /// If `create_new` is set to `false`, then you can try using [`File::hardlink`] to persist the [`File`].
        ///
        /// NOTE that if `self` is a tempfile, then this operation can fail with `io::ErrorKind::Unsupported`.
        pub fn open_tempfile<P: AsRef<Path>>(&self, dir: P) -> Result<File>;
    }
}

mod path {
    pub struct PersistError<O: TempObject> {
        pub error: Error,
        pub temp_path: TempPath<O>,
    }
    impl<O> std::error::Error for PersistError<O> {}
    impl<O> From<PersistError<O>> for Error {}
    impl<O> From<PersistError<O>> for TempPath<O> {}

    pub trait TempObject {
        /// [`io::ErrorKind::AlreadyExists`] should be returned if the `path` already has an object.
        fn create(path: &Path) -> Result<Self>;
        fn keep(&self, path: &Path) -> Result<()>;
        fn persist(&self, new_path: &Path) -> Result<()>;
        fn remove(&mut self, path: &Path) -> Result<()>;
    }
    impl TempObject for File {}
    impl TempObject for DirEntry {}

    #[derive(Debug)]
    pub struct TempPath<O: TempObject> {
        path: PathBuf, 
        object: O,
    }
    impl<O: TempObject> Drop for TempPath<O>  {}
    impl<O: TempObject> TempPath<O>  {
        pub fn new() -> Result<Self>;
        pub fn new_in<P: AsRef<Path>>(p: P) -> Result<Self>;

        pub fn with_prefix<S: AsRef<OsStr>>(prefix: S) -> Result<Self>;
        pub fn with_prefix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
            prefix: S,
            dir: P
        ) -> Result<Self>;

        pub fn path(&self) -> &Path;

        pub fn close(self) -> Result<()>;

        /// Persist temp path with name `new_path`, if `overwrite` is true then the existing object at `new_path`
        /// will be overwritten.
        pub fn persist<P: AsRef<Path>>(
            self,
           new_path: P,
           overwrite: bool,
        ) -> Result<O, PersistError<O>>;

        /// This function could fail since we need to mark the file as non-temporary on some platforms (e.g. windows)
        pub fn keep(self) -> Result<(PathBuf, O), PersistError<O>>;
    }
    impl<O: TempObject> Deref for TempPath<O>  {}
    impl<O: TempObject> DerefMut for TempPath<O>  {}

    pub struct Builder;

    impl<'a, 'b> Builder<'a, 'b> {
        pub fn new() -> Self;

        pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self;
        pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self;
        pub fn rand_bytes(&mut self, rand: usize) -> &mut Self;

        /// [`io::ErrorKind::AlreadyExists`] should be returned if the `path` already has an object.
        pub fn make<F, O>(&self, f: F) -> Result<TempPath<O>>
        where
            F: FnMut(&Path) -> Result<O>,
            O: TempObject;

        /// [`io::ErrorKind::AlreadyExists`] should be returned if the `path` already has an object.
        pub fn make_in<F, O, P>(&self, dir: P, f: F) -> Result<TempPath<O>>
        where
            F: FnMut(&Path) -> Result<O>,
            P: AsRef<Path>,
            O: TempObject;
    }
}

Once we have the basics done, we can add it more methods from tempfile, i.e. the named tempfile, the temp dir, etc.

Links and related work

tempfile, the existing popular implementation of tempfile.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the proposed fs and path APIs in this issue and comparing them with the linked tempfile crate. The scope includes temporary files, paths, persistence, cleanup, and builders, with more methods deferred. Done requires an agreed basic stdlib API and its implementation plan; no files or tests are named.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.