async_tempfile/tempfile.rs
1use std::borrow::{Borrow, BorrowMut};
2use std::fmt::{Debug, Formatter};
3use std::io::{ErrorKind, IoSlice, SeekFrom};
4use std::ops::{Deref, DerefMut};
5use std::path::{Path, PathBuf};
6use std::pin::Pin;
7use std::sync::Arc;
8use std::task::{Context, Poll};
9use tokio::fs::{File, OpenOptions};
10use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
11
12#[cfg(not(feature = "uuid"))]
13use crate::random_name::RandomName;
14use crate::{AtomicOwnership, Error, Ownership, PersistError};
15#[cfg(feature = "uuid")]
16use uuid::Uuid;
17
18pub(crate) const FILE_PREFIX: &str = "atmp_";
19
20/// Maximum number of attempts to find a free name when creating a file with a
21/// randomly generated, collision-resistant name.
22const MAX_NAME_ATTEMPTS: usize = 16;
23
24/// How the underlying file should be opened or created.
25#[derive(Copy, Clone, Eq, PartialEq)]
26enum CreateMode {
27 /// Create a brand-new file, failing if it already exists (`O_EXCL`).
28 /// Used for unpredictable, auto-generated names to avoid clobbering an
29 /// existing file or following a planted symlink.
30 Exclusive,
31 /// Create the file, or open it if it already exists. Used for user-supplied
32 /// names, preserving historic behavior.
33 CreateOrOpen,
34 /// Open an existing file without creating it. Used by `from_existing`.
35 OpenExisting,
36}
37
38/// A named temporary file that will be cleaned automatically
39/// after the last reference to it is dropped.
40pub struct TempFile {
41 /// A local reference to the file. Used to write to or read from the file.
42 ///
43 /// Field order matters: `file` is declared before `core` so that this local
44 /// handle is dropped (closed) before the shared `core` is released and the
45 /// file is deleted. Required for correct deletion on Windows, which refuses
46 /// to delete a file while a handle to it is open.
47 file: File,
48
49 /// A shared pointer to the owned (or non-owned) file.
50 /// The `Arc` ensures that the enclosed file is kept alive
51 /// until all references to it are dropped.
52 core: Arc<TempFileCore>,
53}
54
55/// The instance that tracks the temporary file.
56/// If dropped, the file will be deleted.
57struct TempFileCore {
58 /// The path of the contained file.
59 path: PathBuf,
60
61 /// Whether the file specified in `path` is owned (and deleted on drop) or
62 /// merely borrowed. Stored atomically because it is read from `Drop`, which
63 /// must never block on a lock, and mutated by `keep`/`persist`/`drop_async`.
64 ownership: AtomicOwnership,
65}
66
67impl TempFile {
68 /// Creates a new temporary file in the default location.
69 /// When the instance goes out of scope, the file will be deleted.
70 ///
71 /// ## Example
72 ///
73 /// ```
74 /// # use async_tempfile::{TempFile, Error};
75 /// # use tokio::fs;
76 /// # let _ = tokio_test::block_on(async {
77 /// let file = TempFile::new().await?;
78 ///
79 /// // The file exists.
80 /// let file_path = file.file_path().clone();
81 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
82 ///
83 /// // Deletes the file.
84 /// drop(file);
85 ///
86 /// // The file was removed.
87 /// assert!(fs::metadata(file_path).await.is_err());
88 /// # Ok::<(), Error>(())
89 /// # });
90 /// ```
91 pub async fn new() -> Result<Self, Error> {
92 Self::new_in(Self::default_dir()).await
93 }
94
95 /// Creates a new temporary file in the default location.
96 /// When the instance goes out of scope, the file will be deleted.
97 ///
98 /// ## Arguments
99 ///
100 /// * `name` - The name of the file to create in the default temporary directory.
101 ///
102 /// ## Example
103 ///
104 /// ```
105 /// # use async_tempfile::{TempFile, Error};
106 /// # use tokio::fs;
107 /// # let _ = tokio_test::block_on(async {
108 /// let file = TempFile::new_with_name("new_with_name_example.file").await?;
109 ///
110 /// // The file exists.
111 /// let file_path = file.file_path().clone();
112 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
113 ///
114 /// // Deletes the file.
115 /// drop(file);
116 ///
117 /// // The file was removed.
118 /// assert!(fs::metadata(file_path).await.is_err());
119 /// # Ok::<(), Error>(())
120 /// # });
121 /// ```
122 pub async fn new_with_name<N: AsRef<str>>(name: N) -> Result<Self, Error> {
123 Self::new_with_name_in(name, Self::default_dir()).await
124 }
125
126 /// Creates a new temporary file in the default location.
127 /// When the instance goes out of scope, the file will be deleted.
128 ///
129 /// ## Arguments
130 ///
131 /// * `uuid` - A UUID to use as a suffix to the file name.
132 ///
133 /// ## Example
134 ///
135 /// ```
136 /// # use async_tempfile::{TempFile, Error};
137 /// # use tokio::fs;
138 /// # let _ = tokio_test::block_on(async {
139 /// let id = uuid::Uuid::new_v4();
140 /// let file = TempFile::new_with_uuid(id).await?;
141 ///
142 /// // The file exists.
143 /// let file_path = file.file_path().clone();
144 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
145 ///
146 /// // Deletes the file.
147 /// drop(file);
148 ///
149 /// // The file was removed.
150 /// assert!(fs::metadata(file_path).await.is_err());
151 /// # Ok::<(), Error>(())
152 /// # });
153 /// ```
154 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
155 #[cfg(feature = "uuid")]
156 pub async fn new_with_uuid(uuid: Uuid) -> Result<Self, Error> {
157 Self::new_with_uuid_in(uuid, Self::default_dir()).await
158 }
159
160 /// Creates a new temporary file in the specified location.
161 /// When the instance goes out of scope, the file will be deleted.
162 ///
163 /// The file is created with a collision-resistant, unpredictable name using
164 /// an exclusive (`O_EXCL`) create, so it never clobbers an existing file.
165 ///
166 /// ## Crate Features
167 ///
168 /// * `uuid` - When the `uuid` crate feature is enabled, a random UUIDv4 is used to
169 /// generate the temporary file name.
170 ///
171 /// ## Arguments
172 ///
173 /// * `dir` - The directory to create the file in.
174 ///
175 /// ## Example
176 ///
177 /// ```
178 /// # use async_tempfile::{TempFile, Error};
179 /// # use tokio::fs;
180 /// # let _ = tokio_test::block_on(async {
181 /// let path = std::env::temp_dir();
182 /// let file = TempFile::new_in(path).await?;
183 ///
184 /// // The file exists.
185 /// let file_path = file.file_path().clone();
186 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
187 ///
188 /// // Deletes the file.
189 /// drop(file);
190 ///
191 /// // The file was removed.
192 /// assert!(fs::metadata(file_path).await.is_err());
193 /// # Ok::<(), Error>(())
194 /// # });
195 pub async fn new_in<P: Borrow<Path>>(dir: P) -> Result<Self, Error> {
196 Self::create_with_affixes(dir.borrow(), FILE_PREFIX, "").await
197 }
198
199 /// Creates a new temporary file in the specified location.
200 /// When the instance goes out of scope, the file will be deleted.
201 ///
202 /// ## Arguments
203 ///
204 /// * `dir` - The directory to create the file in.
205 /// * `name` - The file name to use.
206 ///
207 /// ## Example
208 ///
209 /// ```
210 /// # use async_tempfile::{TempFile, Error};
211 /// # use tokio::fs;
212 /// # let _ = tokio_test::block_on(async {
213 /// let path = std::env::temp_dir();
214 /// let file = TempFile::new_with_name_in("new_with_name_in_example.file", path).await?;
215 ///
216 /// // The file exists.
217 /// let file_path = file.file_path().clone();
218 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
219 ///
220 /// // Deletes the file.
221 /// drop(file);
222 ///
223 /// // The file was removed.
224 /// assert!(fs::metadata(file_path).await.is_err());
225 /// # Ok::<(), Error>(())
226 /// # });
227 /// ```
228 pub async fn new_with_name_in<N: AsRef<str>, P: Borrow<Path>>(
229 name: N,
230 dir: P,
231 ) -> Result<Self, Error> {
232 let dir = dir.borrow();
233 if !crate::path_is_dir(dir).await {
234 return Err(Error::InvalidDirectory);
235 }
236 let path = dir.join(name.as_ref());
237 Self::new_internal(path, Ownership::Owned, CreateMode::CreateOrOpen).await
238 }
239
240 /// Creates a new temporary file in the specified location.
241 /// When the instance goes out of scope, the file will be deleted.
242 ///
243 /// ## Arguments
244 ///
245 /// * `dir` - The directory to create the file in.
246 /// * `uuid` - A UUID to use as a suffix to the file name.
247 ///
248 /// ## Example
249 ///
250 /// ```
251 /// # use async_tempfile::{TempFile, Error};
252 /// # use tokio::fs;
253 /// # let _ = tokio_test::block_on(async {
254 /// let path = std::env::temp_dir();
255 /// let id = uuid::Uuid::new_v4();
256 /// let file = TempFile::new_with_uuid_in(id, path).await?;
257 ///
258 /// // The file exists.
259 /// let file_path = file.file_path().clone();
260 /// assert!(fs::metadata(file_path.clone()).await.is_ok());
261 ///
262 /// // Deletes the file.
263 /// drop(file);
264 ///
265 /// // The file was removed.
266 /// assert!(fs::metadata(file_path).await.is_err());
267 /// # Ok::<(), Error>(())
268 /// # });
269 /// ```
270 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
271 #[cfg(feature = "uuid")]
272 pub async fn new_with_uuid_in<P: Borrow<Path>>(uuid: Uuid, dir: P) -> Result<Self, Error> {
273 let file_name = format!("{FILE_PREFIX}{uuid}");
274 Self::new_with_name_in(file_name, dir).await
275 }
276
277 /// Wraps a new instance of this type around an existing file.
278 /// If `ownership` is set to [`Ownership::Borrowed`], this method does not take ownership of
279 /// the file, i.e. the file will not be deleted when the instance is dropped.
280 ///
281 /// ## Arguments
282 ///
283 /// * `path` - The path of the file to wrap.
284 /// * `ownership` - The ownership of the file.
285 pub async fn from_existing<P: Borrow<Path>>(
286 path: P,
287 ownership: Ownership,
288 ) -> Result<Self, Error> {
289 if !crate::path_is_file(path.borrow()).await {
290 return Err(Error::InvalidFile);
291 }
292 Self::new_internal(path, ownership, CreateMode::OpenExisting).await
293 }
294
295 /// Creates a builder for configuring a new temporary file
296 /// (prefix, suffix, directory) before creating it.
297 ///
298 /// ## Example
299 ///
300 /// ```
301 /// # use async_tempfile::{TempFile, Error};
302 /// # let _ = tokio_test::block_on(async {
303 /// let file = TempFile::builder()
304 /// .prefix("log_")
305 /// .suffix(".txt")
306 /// .create()
307 /// .await?;
308 ///
309 /// let name = file.file_path().file_name().unwrap().to_string_lossy().into_owned();
310 /// assert!(name.starts_with("log_"));
311 /// assert!(name.ends_with(".txt"));
312 /// # Ok::<(), Error>(())
313 /// # });
314 /// ```
315 pub fn builder() -> crate::TempFileBuilder {
316 crate::TempFileBuilder::new()
317 }
318
319 /// Returns the path of the underlying temporary file.
320 pub fn file_path(&self) -> &PathBuf {
321 &self.core.path
322 }
323
324 /// Opens a new TempFile instance in read-write mode.
325 pub async fn open_rw(&self) -> Result<TempFile, Error> {
326 let file = OpenOptions::new()
327 .read(true)
328 .write(true)
329 .open(&self.core.path)
330 .await?;
331 Ok(TempFile {
332 file,
333 core: self.core.clone(),
334 })
335 }
336
337 /// Opens a new TempFile instance in read-only mode.
338 pub async fn open_ro(&self) -> Result<TempFile, Error> {
339 let file = OpenOptions::new()
340 .read(true)
341 .write(false)
342 .open(&self.core.path)
343 .await?;
344 Ok(TempFile {
345 file,
346 core: self.core.clone(),
347 })
348 }
349
350 /// Creates a new TempFile instance that shares the same underlying
351 /// file handle as the existing TempFile instance.
352 /// Reads, writes, and seeks will affect both TempFile instances simultaneously.
353 pub async fn try_clone(&self) -> Result<TempFile, Error> {
354 Ok(TempFile {
355 file: self.file.try_clone().await?,
356 core: self.core.clone(),
357 })
358 }
359
360 /// Determines the ownership of the temporary file.
361 /// ### Example
362 /// ```
363 /// # use async_tempfile::{Ownership, TempFile};
364 /// # let _ = tokio_test::block_on(async {
365 /// let file = TempFile::new().await?;
366 /// assert_eq!(file.ownership(), Ownership::Owned);
367 /// # drop(file);
368 /// # Ok::<(), Box<dyn std::error::Error>>(())
369 /// # });
370 /// ```
371 pub fn ownership(&self) -> Ownership {
372 self.core.ownership.get()
373 }
374
375 /// Disables automatic deletion and returns the path of the underlying file,
376 /// turning the temporary file into a permanent one.
377 ///
378 /// This affects every clone that shares the same underlying file: none of
379 /// them will delete it when dropped.
380 ///
381 /// ## Example
382 ///
383 /// ```
384 /// # use async_tempfile::{TempFile, Error};
385 /// # use tokio::fs;
386 /// # let _ = tokio_test::block_on(async {
387 /// let file = TempFile::new().await?;
388 /// let path = file.keep();
389 ///
390 /// // The file still exists after the handle is dropped.
391 /// assert!(fs::metadata(path.clone()).await.is_ok());
392 /// # fs::remove_file(path).await.ok();
393 /// # Ok::<(), Error>(())
394 /// # });
395 /// ```
396 pub fn keep(self) -> PathBuf {
397 let path = self.core.path.clone();
398 self.core.ownership.set_borrowed();
399 path
400 }
401
402 /// Persists the temporary file by moving it to `target`, returning the new
403 /// path. The file will no longer be deleted automatically.
404 ///
405 /// The move is performed with [`tokio::fs::rename`] and therefore must stay
406 /// on the same filesystem (a cross-device move returns an error).
407 ///
408 /// On failure the temporary file is **not** deleted: it is left at its
409 /// original location and that path is returned in [`PersistError::path`], so
410 /// no data is lost on a cross-device or permission error. The caller may
411 /// re-wrap it with [`TempFile::from_existing`] to restore automatic cleanup,
412 /// or delete it. The local handle is closed before the rename so the move
413 /// also succeeds on Windows.
414 ///
415 /// ## Arguments
416 ///
417 /// * `target` - The destination path to move the file to.
418 ///
419 /// ## Example
420 ///
421 /// ```
422 /// # use async_tempfile::{TempFile, Error};
423 /// # use tokio::fs;
424 /// # let _ = tokio_test::block_on(async {
425 /// let file = TempFile::new().await?;
426 /// let target = std::env::temp_dir().join("persisted_async_tempfile.txt");
427 ///
428 /// let path = file.persist(&target).await.map_err(|e| e.error)?;
429 /// assert!(fs::metadata(path.clone()).await.is_ok());
430 /// # fs::remove_file(path).await.ok();
431 /// # Ok::<(), Error>(())
432 /// # });
433 /// ```
434 pub async fn persist<P: AsRef<Path>>(self, target: P) -> Result<PathBuf, PersistError> {
435 let target = target.as_ref().to_path_buf();
436 let TempFile { file, core } = self;
437 // Close our handle before the rename: Windows refuses to move a file
438 // while a handle to it is open.
439 drop(file);
440
441 match tokio::fs::rename(&core.path, &target).await {
442 Ok(()) => {
443 core.ownership.set_borrowed();
444 Ok(target)
445 }
446 Err(e) => {
447 // Preserve the caller's data: leave the temporary in place and
448 // report where it is, rather than deleting it on the way out.
449 core.ownership.set_borrowed();
450 Err(PersistError {
451 error: Error::Io(e),
452 path: core.path.clone(),
453 })
454 }
455 }
456 }
457
458 /// Asynchronously drops the TempFile, ensuring any resources are properly released.
459 /// This is useful for explicitly managing the lifecycle of the TempFile
460 /// in an asynchronous context.
461 ///
462 /// When this is the last reference to an owned file, the file is removed via
463 /// [`tokio::fs::remove_file`] without blocking the runtime. The synchronous
464 /// `Drop` remains armed as a backstop, so a cancelled or panicking
465 /// `drop_async` still cleans up the file.
466 ///
467 /// ## Example
468 ///
469 /// ```rust
470 /// # use async_tempfile::{TempFile, Error};
471 /// # let _ = tokio_test::block_on(async {
472 /// let file = TempFile::new().await?;
473 /// let path = file.file_path().to_path_buf();
474 /// assert!(path.is_file());
475 ///
476 /// file.drop_async().await; // Explicitly drop the TempFile
477 ///
478 /// assert!(!path.exists());
479 /// # Ok::<(), Error>(())
480 /// # });
481 /// ```
482 pub async fn drop_async(self) {
483 let TempFile { file, core } = self;
484 // Close the local read-write handle before attempting deletion.
485 drop(file);
486
487 // Only the sole owner removes the file asynchronously; otherwise the
488 // remaining references' `Drop` impls handle cleanup.
489 let Some(core) = Arc::into_inner(core) else {
490 return;
491 };
492
493 if core.ownership.is_owned() {
494 // `core` is still marked owned here. If this future is cancelled or
495 // panics at the await point, `core` is dropped and its synchronous
496 // `Drop` deletes the file. We disarm only after a confirmed removal.
497 match tokio::fs::remove_file(&core.path).await {
498 Ok(()) => core.ownership.set_borrowed(),
499 Err(e) if e.kind() == ErrorKind::NotFound => core.ownership.set_borrowed(),
500 // Leave armed: the synchronous `Drop` below retries the removal.
501 Err(_) => {}
502 }
503 }
504
505 drop(core);
506 }
507
508 /// Closes the file, deleting it when this is the last reference to an owned
509 /// file, and **returns the removal result** so the caller can observe a
510 /// deletion failure - unlike the implicit `Drop`, which has no way to report
511 /// one. This is the synchronous sibling of [`drop_async`](Self::drop_async);
512 /// prefer `drop_async` inside an async context to avoid blocking the runtime
513 /// on the `unlink` syscall.
514 ///
515 /// If other clones still reference the file, cleanup is left to them and
516 /// `Ok(())` is returned. On error the file is left in place and the error is
517 /// returned; no further automatic deletion is attempted (a synchronous retry
518 /// of the same failing syscall would be pointless), so the caller owns the
519 /// file and may inspect, retry, or remove it. This differs from
520 /// [`drop_async`](Self::drop_async), whose synchronous `Drop` backstop is a
521 /// genuinely different deletion mechanism after a possibly-cancelled async
522 /// removal.
523 ///
524 /// ## Example
525 ///
526 /// ```rust
527 /// # use async_tempfile::{TempFile, Error};
528 /// # let _ = tokio_test::block_on(async {
529 /// let file = TempFile::new().await?;
530 /// let path = file.file_path().to_path_buf();
531 ///
532 /// file.close()?; // Explicitly close, surfacing any deletion error.
533 ///
534 /// assert!(!path.exists());
535 /// # Ok::<(), Error>(())
536 /// # });
537 /// ```
538 pub fn close(self) -> std::io::Result<()> {
539 let TempFile { file, core } = self;
540 // Close the local read-write handle before attempting deletion (matters
541 // on platforms that lock open files, such as Windows).
542 drop(file);
543
544 // Only the sole owner removes the file; otherwise the remaining
545 // references' `Drop` impls handle cleanup.
546 let Some(core) = Arc::into_inner(core) else {
547 return Ok(());
548 };
549
550 if core.ownership.is_owned() {
551 match std::fs::remove_file(&core.path) {
552 Ok(()) => core.ownership.set_borrowed(),
553 Err(e) if e.kind() == ErrorKind::NotFound => core.ownership.set_borrowed(),
554 // Disarm and surface the error: leaving `core` armed would make
555 // the trailing `Drop` retry the identical syscall for nothing.
556 // The file is left in place for the caller to handle.
557 Err(e) => {
558 core.ownership.set_borrowed();
559 return Err(e);
560 }
561 }
562 }
563
564 Ok(())
565 }
566
567 /// Creates a file named `{prefix}{random}{suffix}` with an unpredictable,
568 /// collision-resistant random core, using an exclusive (`O_EXCL`) create and
569 /// retrying on the (astronomically unlikely) collision. Shared by `new_in`
570 /// and [`crate::TempFileBuilder`].
571 pub(crate) async fn create_with_affixes(
572 dir: &Path,
573 prefix: &str,
574 suffix: &str,
575 ) -> Result<Self, Error> {
576 if !crate::path_is_dir(dir).await {
577 return Err(Error::InvalidDirectory);
578 }
579 // Affixes are filename fragments, not paths: a separator would let the
580 // composed name escape `dir` (`../` traversal, or an absolute prefix
581 // replacing it via `Path::join`).
582 if !crate::affix_is_safe(prefix) || !crate::affix_is_safe(suffix) {
583 return Err(Error::InvalidAffix);
584 }
585 let mut last_err = None;
586 for _ in 0..MAX_NAME_ATTEMPTS {
587 let name = format!("{prefix}{}{suffix}", Self::random_core_name());
588 match Self::new_internal(dir.join(name), Ownership::Owned, CreateMode::Exclusive).await
589 {
590 Ok(file) => return Ok(file),
591 Err(Error::Io(e)) if e.kind() == ErrorKind::AlreadyExists => {
592 last_err = Some(Error::Io(e));
593 }
594 Err(e) => return Err(e),
595 }
596 }
597 Err(last_err.unwrap_or(Error::InvalidFile))
598 }
599
600 /// Generates the unpredictable, collision-resistant random core of a name,
601 /// without any prefix or suffix.
602 fn random_core_name() -> String {
603 #[cfg(feature = "uuid")]
604 {
605 Uuid::new_v4().to_string()
606 }
607
608 #[cfg(not(feature = "uuid"))]
609 {
610 RandomName::new("").as_str().to_string()
611 }
612 }
613
614 async fn new_internal<P: Borrow<Path>>(
615 path: P,
616 ownership: Ownership,
617 mode: CreateMode,
618 ) -> Result<Self, Error> {
619 let path = path.borrow();
620
621 // A single read-write handle both creates (per `mode`) and serves the
622 // file. Keeping just this one handle alive holds the inode open for the
623 // lifetime of the (shared) core, so no separate keep-alive handle is
624 // needed - that only wasted a file descriptor per file.
625 let mut options = OpenOptions::new();
626 options.read(true).write(true);
627 match mode {
628 CreateMode::Exclusive => {
629 options.create_new(true);
630 }
631 CreateMode::CreateOrOpen => {
632 options.create(true);
633 }
634 CreateMode::OpenExisting => {}
635 }
636
637 let file = options.open(path).await?;
638 let core = TempFileCore {
639 ownership: AtomicOwnership::new(ownership),
640 path: PathBuf::from(path),
641 };
642
643 Ok(Self {
644 file,
645 core: Arc::new(core),
646 })
647 }
648
649 /// Gets the default temporary file directory.
650 #[inline(always)]
651 fn default_dir() -> PathBuf {
652 std::env::temp_dir()
653 }
654}
655
656/// Ensures that the underlying file is deleted if this is an owned instance.
657/// If the underlying file is not owned, this operation does nothing.
658impl Drop for TempFileCore {
659 fn drop(&mut self) {
660 // Ensure we don't drop borrowed files. Read via the lock-free atomic:
661 // `Drop` may run on a runtime worker thread and must never block.
662 if !self.ownership.is_owned() {
663 return;
664 }
665
666 // The owning `TempFile`'s handle (declared before `core`) has already
667 // been closed by the time this runs, so the file can be deleted even on
668 // platforms that lock open files (Windows).
669 //
670 // Synchronous on purpose: `Drop` must not re-enter the async runtime, as
671 // it may run on a runtime worker thread. Use `drop_async` for an async
672 // deletion path at an explicit await point.
673 let _ = std::fs::remove_file(&self.path);
674 }
675}
676
677impl Debug for TempFileCore {
678 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
679 write!(f, "{:?}", self.path)
680 }
681}
682
683impl Debug for TempFile {
684 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
685 write!(f, "{:?}", self.core)
686 }
687}
688
689/// Allows implicit treatment of TempFile as a File.
690impl Deref for TempFile {
691 type Target = File;
692
693 fn deref(&self) -> &Self::Target {
694 &self.file
695 }
696}
697
698/// Allows implicit treatment of TempFile as a mutable File.
699impl DerefMut for TempFile {
700 fn deref_mut(&mut self) -> &mut File {
701 &mut self.file
702 }
703}
704
705impl Borrow<File> for TempFile {
706 fn borrow(&self) -> &File {
707 &self.file
708 }
709}
710
711impl BorrowMut<File> for TempFile {
712 fn borrow_mut(&mut self) -> &mut File {
713 &mut self.file
714 }
715}
716
717impl AsRef<File> for TempFile {
718 fn as_ref(&self) -> &File {
719 &self.file
720 }
721}
722
723/// Forwarding AsyncWrite to the embedded File
724impl AsyncWrite for TempFile {
725 fn poll_write(
726 mut self: Pin<&mut Self>,
727 cx: &mut Context<'_>,
728 buf: &[u8],
729 ) -> Poll<Result<usize, std::io::Error>> {
730 Pin::new(&mut self.file).poll_write(cx, buf)
731 }
732
733 fn poll_flush(
734 mut self: Pin<&mut Self>,
735 cx: &mut Context<'_>,
736 ) -> Poll<Result<(), std::io::Error>> {
737 Pin::new(&mut self.file).poll_flush(cx)
738 }
739
740 fn poll_shutdown(
741 mut self: Pin<&mut Self>,
742 cx: &mut Context<'_>,
743 ) -> Poll<Result<(), std::io::Error>> {
744 Pin::new(&mut self.file).poll_shutdown(cx)
745 }
746
747 fn poll_write_vectored(
748 mut self: Pin<&mut Self>,
749 cx: &mut Context<'_>,
750 bufs: &[IoSlice<'_>],
751 ) -> Poll<Result<usize, std::io::Error>> {
752 Pin::new(&mut self.file).poll_write_vectored(cx, bufs)
753 }
754}
755
756/// Forwarding AsyncRead to the embedded File
757impl AsyncRead for TempFile {
758 fn poll_read(
759 mut self: Pin<&mut Self>,
760 cx: &mut Context<'_>,
761 buf: &mut ReadBuf<'_>,
762 ) -> Poll<std::io::Result<()>> {
763 Pin::new(&mut self.file).poll_read(cx, buf)
764 }
765}
766
767/// Forwarding AsyncSeek to the embedded File
768impl AsyncSeek for TempFile {
769 fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> {
770 Pin::new(&mut self.file).start_seek(position)
771 }
772
773 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<u64>> {
774 Pin::new(&mut self.file).poll_complete(cx)
775 }
776}
777
778#[cfg(test)]
779mod tests {
780 use super::*;
781 use crate::random_name::RandomName;
782
783 #[test]
784 fn test_random_name() {
785 let name = RandomName::new(FILE_PREFIX);
786 assert!(name.as_ref().starts_with(FILE_PREFIX))
787 }
788}