1use std::{io::Write, path::Path};
2
3use tempfile::{NamedTempFile, TempPath};
4
5use crate::{handle, AutoRemove};
6
7enum TempfileOrTemppath {
8 Tempfile(NamedTempFile),
9 Temppath(TempPath),
10}
11
12pub(crate) struct ForksafeTempfile {
13 inner: TempfileOrTemppath,
14 cleanup: AutoRemove,
15 pub owning_process_id: u32,
16}
17
18impl ForksafeTempfile {
19 pub fn new(tempfile: NamedTempFile, cleanup: AutoRemove, mode: handle::Mode) -> Self {
20 use handle::Mode::*;
21 ForksafeTempfile {
22 inner: match mode {
23 Closed => TempfileOrTemppath::Temppath(tempfile.into_temp_path()),
24 Writable => TempfileOrTemppath::Tempfile(tempfile),
25 },
26 cleanup,
27 owning_process_id: std::process::id(),
28 }
29 }
30}
31
32impl ForksafeTempfile {
33 pub fn as_mut_tempfile(&mut self) -> Option<&mut NamedTempFile> {
34 match &mut self.inner {
35 TempfileOrTemppath::Tempfile(file) => Some(file),
36 TempfileOrTemppath::Temppath(_) => None,
37 }
38 }
39 pub fn close(self) -> Self {
40 if let TempfileOrTemppath::Tempfile(file) = self.inner {
41 ForksafeTempfile {
42 inner: TempfileOrTemppath::Temppath(file.into_temp_path()),
43 cleanup: self.cleanup,
44 owning_process_id: self.owning_process_id,
45 }
46 } else {
47 self
48 }
49 }
50 pub fn persist(mut self, path: impl AsRef<Path>) -> Result<Option<std::fs::File>, (std::io::Error, Self)> {
51 match self.inner {
52 TempfileOrTemppath::Tempfile(file) => match file.persist(path) {
53 Ok(file) => Ok(Some(file)),
54 Err(err) => Err((err.error, {
55 self.inner = TempfileOrTemppath::Tempfile(err.file);
56 self
57 })),
58 },
59 TempfileOrTemppath::Temppath(temppath) => match temppath.persist(path) {
60 Ok(_) => Ok(None),
61 Err(err) => Err((err.error, {
62 self.inner = TempfileOrTemppath::Temppath(err.path);
63 self
64 })),
65 },
66 }
67 }
68
69 pub fn into_temppath(self) -> TempPath {
70 match self.inner {
71 TempfileOrTemppath::Tempfile(file) => file.into_temp_path(),
72 TempfileOrTemppath::Temppath(path) => path,
73 }
74 }
75 pub fn into_tempfile(self) -> Option<NamedTempFile> {
76 match self.inner {
77 TempfileOrTemppath::Tempfile(file) => Some(file),
78 TempfileOrTemppath::Temppath(_) => None,
79 }
80 }
81 pub fn drop_impl(self) {
82 let file_path = match self.inner {
83 TempfileOrTemppath::Tempfile(file) => file.path().to_owned(),
84 TempfileOrTemppath::Temppath(path) => path.to_path_buf(),
85 };
86 let parent_directory = file_path.parent().expect("every tempfile has a parent directory");
87 self.cleanup.execute_best_effort(parent_directory);
88 }
89
90 pub fn drop_without_deallocation(self) {
91 let temppath = match self.inner {
92 TempfileOrTemppath::Tempfile(file) => {
93 let (mut file, temppath) = file.into_parts();
94 file.flush().ok();
95 temppath
96 }
97 TempfileOrTemppath::Temppath(path) => path,
98 };
99 std::fs::remove_file(&temppath).ok();
100 std::mem::forget(
101 self.cleanup
102 .execute_best_effort(temppath.parent().expect("every file has a directory")),
103 );
104 std::mem::forget(temppath); }
106}