fencryption_lib/
tmp.rs

1//! Handle temporary files and directories.
2
3use std::{
4    env,
5    fs::{self, File, OpenOptions, ReadDir},
6    io,
7    path::{Path, PathBuf},
8};
9
10/// Handle a temporary directory.
11///
12/// The `path` parameter (present in some methods) must be
13/// relative because it will be joined to the temporary
14/// directory path.
15///
16/// When this struct is dropped, the temporary directory
17/// itself is automatically deleted.
18pub struct TmpDir(PathBuf);
19
20impl TmpDir {
21    /// Create a new TmpDir instance.
22    pub fn new() -> Result<Self, io::Error> {
23        let path = env::temp_dir().join(uuid::Uuid::new_v4().to_string());
24        fs::create_dir(&path)?;
25        Ok(TmpDir(path))
26    }
27
28    /// Path of the temporary directory.
29    pub fn path(&self) -> PathBuf {
30        self.0.clone()
31    }
32
33    /// Generate a new unique path in the temporary directory.
34    pub fn unique_path(&self) -> PathBuf {
35        self.0.join(uuid::Uuid::new_v4().to_string())
36    }
37
38    /// Write to a file (or create it if it doesn't exist)
39    /// in the temporary directory. Akin to [`fs::write`].
40    pub fn write_file<P, C>(&self, path: P, contents: C) -> io::Result<()>
41    where
42        P: AsRef<Path>,
43        C: AsRef<[u8]>,
44    {
45        fs::write(self.0.join(path), contents)
46    }
47
48    /// Read a file in the temporary directory. Akin to
49    /// [`fs::read`].
50    pub fn read_file<P>(&self, path: P) -> io::Result<Vec<u8>>
51    where
52        P: AsRef<Path>,
53    {
54        fs::read(self.0.join(path))
55    }
56
57    /// Create a directory inside the temporary directory.
58    /// Akin to [`fs::create_dir`].
59    pub fn create_dir<P>(&self, path: P) -> io::Result<()>
60    where
61        P: AsRef<Path>,
62    {
63        fs::create_dir(self.0.join(path))
64    }
65
66    /// Create a directory and all of its parent components
67    /// if they are missing. Akin to [`fs::create_dir_all`].
68    pub fn create_dir_all<P>(&self, path: P) -> io::Result<()>
69    where
70        P: AsRef<Path>,
71    {
72        fs::create_dir_all(self.0.join(path))
73    }
74
75    /// Create a file in the temporary directory and open it
76    /// in write-only mode. Akin to [`File::create`].
77    pub fn create_file<P>(&self, path: P) -> io::Result<File>
78    where
79        P: AsRef<Path>,
80    {
81        File::create(self.0.join(path))
82    }
83
84    /// Open a file in the temporary directory in read-only
85    /// mode. Akin to [`File::open`].
86    pub fn open_readable<P>(&self, path: P) -> io::Result<File>
87    where
88        P: AsRef<Path>,
89    {
90        File::open(self.0.join(path))
91    }
92
93    /// Open a file in the temporary directory in write-only
94    /// mode.
95    pub fn open_writable<P>(&self, path: P) -> io::Result<File>
96    where
97        P: AsRef<Path>,
98    {
99        OpenOptions::new().write(true).open(self.0.join(path))
100    }
101
102    /// Open a file in the temporary directory using the
103    /// provided OpenOptions. Akin to [`fs::OpenOptions::open`].
104    pub fn open_with_opts<P>(&self, opts: &mut OpenOptions, path: P) -> io::Result<File>
105    where
106        P: AsRef<Path>,
107    {
108        opts.open(self.0.join(path))
109    }
110
111    /// Get metadata for the given path. Akin to [`fs::metadata`].
112    pub fn metadata<P>(&self, path: P) -> io::Result<fs::Metadata>
113    where
114        P: AsRef<Path>,
115    {
116        self.0.join(path.as_ref()).metadata()
117    }
118
119    /// Check if a path exists in the current directory. Akin
120    /// to [`Path::exists`].
121    pub fn exists<P>(&self, path: P) -> bool
122    where
123        P: AsRef<Path>,
124    {
125        self.0.join(path.as_ref()).exists()
126    }
127
128    /// Read temporary directory. Akin to [`fs::read_dir`].
129    pub fn read_dir<P>(&self, path: P) -> io::Result<ReadDir>
130    where
131        P: AsRef<Path>,
132    {
133        fs::read_dir(&self.0.join(path))
134    }
135
136    /// Remove a file in the current directory. Akin to
137    /// [`fs::remove_file`].
138    pub fn remove_file<P>(&self, path: P) -> io::Result<()>
139    where
140        P: AsRef<Path>,
141    {
142        fs::remove_file(&self.0.join(path))
143    }
144
145    /// Remove a directory in the current directory. Akin to
146    /// [`fs::remove_dir`].
147    pub fn remove_dir<P>(&self, path: P) -> io::Result<()>
148    where
149        P: AsRef<Path>,
150    {
151        fs::remove_dir(&self.0.join(path))
152    }
153
154    /// Remove a directory and all its contents in the current
155    /// directory. Akin to [`fs::remove_dir_all`].
156    pub fn remove_dir_all<P>(&self, path: P) -> io::Result<()>
157    where
158        P: AsRef<Path>,
159    {
160        fs::remove_dir_all(&self.0.join(path))
161    }
162}
163
164impl Drop for TmpDir {
165    /// When TmpDir is dropped, the temporary directory
166    /// itself is deleted.
167    fn drop(&mut self) {
168        fs::remove_dir_all(&self.0).ok();
169    }
170}
171
172/// Handle a temporary file.
173///
174/// When this struct is dropped, the temporary file itself is
175/// automatically deleted.
176pub struct TmpFile(PathBuf);
177
178impl TmpFile {
179    /// Create a new TmpFile instance.
180    pub fn new() -> Result<Self, io::Error> {
181        let path = env::temp_dir().join(uuid::Uuid::new_v4().to_string());
182        fs::write(&path, &[])?;
183        Ok(TmpFile(path))
184    }
185
186    /// Path of the temporary file.
187    pub fn path(&self) -> PathBuf {
188        self.0.clone()
189    }
190
191    /// Write to the temporary file. Akin to [`fs::write`].
192    pub fn write<C>(&self, contents: C) -> io::Result<()>
193    where
194        C: AsRef<[u8]>,
195    {
196        fs::write(&self.0, contents)
197    }
198
199    /// Read the temporary file. Akin to [`fs::read`].
200    pub fn read(&self) -> io::Result<Vec<u8>> {
201        fs::read(&self.0)
202    }
203
204    /// Open the temporary file in read-only mode. Akin to
205    /// [`File::open`].
206    pub fn open_readable(&self) -> io::Result<File> {
207        File::open(&self.0)
208    }
209
210    /// Open the temporary file in write-only mode.
211    pub fn open_writable(&self) -> io::Result<File> {
212        OpenOptions::new().write(true).open(&self.0)
213    }
214
215    /// Open the temporary file using the provided OpenOptions.
216    /// Akin to [`fs::OpenOptions::open`].
217    pub fn open_with_opts(&self, opts: &mut OpenOptions) -> io::Result<File> {
218        opts.open(&self.0)
219    }
220}
221
222impl Drop for TmpFile {
223    /// When TmpFile is dropped, the temporary file itself is
224    /// deleted.
225    fn drop(&mut self) {
226        fs::remove_dir_all(&self.0).ok();
227    }
228}