Skip to main content

async_fs_io/
temp.rs

1//! Explicitly asynchronous temporary files and directories.
2
3use std::path::{Path, PathBuf};
4
5use crate::{AsyncFile, FsError, Operation};
6
7/// An asynchronously created temporary directory.
8pub struct TempDir {
9    path: PathBuf,
10}
11
12impl TempDir {
13    /// Create a uniquely named directory below `root`.
14    pub async fn create(root: impl AsRef<Path>) -> Result<Self, FsError> {
15        let root = root.as_ref().to_owned();
16        tokio::fs::create_dir_all(&root)
17            .await
18            .map_err(|error| FsError::io(Operation::Directory, &root, error))?;
19        let path = root.join(format!("tmp-{}", uuid::Uuid::new_v4()));
20        tokio::fs::create_dir(&path)
21            .await
22            .map_err(|error| FsError::io(Operation::Directory, &path, error))?;
23        Ok(Self { path })
24    }
25
26    /// Return the directory path.
27    #[must_use]
28    pub fn path(&self) -> &Path {
29        &self.path
30    }
31
32    /// Remove the directory and all its contents asynchronously.
33    pub async fn remove(self) -> Result<(), FsError> {
34        crate::directory::remove_dir_all(&self.path).await
35    }
36}
37
38/// An asynchronously created temporary file with explicit cleanup.
39pub struct TempFile {
40    path: PathBuf,
41    file: Option<AsyncFile>,
42}
43
44impl TempFile {
45    /// Create a uniquely named temporary file below `root`.
46    pub async fn create(root: impl AsRef<Path>) -> Result<Self, FsError> {
47        let root = root.as_ref().to_owned();
48        tokio::fs::create_dir_all(&root)
49            .await
50            .map_err(|error| FsError::io(Operation::Directory, &root, error))?;
51        let path = root.join(format!("tmp-file-{}", uuid::Uuid::new_v4()));
52        let file = AsyncFile::create_new(&path).await?;
53        Ok(Self {
54            path,
55            file: Some(file),
56        })
57    }
58
59    /// Return the temporary file path.
60    #[must_use]
61    pub fn path(&self) -> &Path {
62        &self.path
63    }
64
65    /// Borrow the open async file.
66    pub fn file_mut(&mut self) -> Result<&mut AsyncFile, FsError> {
67        self.file.as_mut().ok_or_else(|| {
68            FsError::InvalidRequest("temporary file handle was already closed".to_owned())
69        })
70    }
71
72    /// Close the handle and remove the file asynchronously.
73    pub async fn remove(mut self) -> Result<(), FsError> {
74        self.file.take();
75        tokio::fs::remove_file(&self.path)
76            .await
77            .map_err(|error| FsError::io(Operation::Write, &self.path, error))
78    }
79
80    /// Close the handle while retaining the file on disk.
81    pub fn persist(mut self) -> PathBuf {
82        self.file.take();
83        self.path
84    }
85}