rust-lang / rust-lang/libs-team
Add basic tempfile API to stdlib
Nobody has claimed this yet.
- 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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