1#[cfg(feature = "download-to-file")]
2mod download_to_file;
3#[cfg(feature = "download-to-file")]
4pub use self::download_to_file::download_to_file;
5
6#[cfg(feature = "drop-remove-path")]
7mod drop_remove_path;
8#[cfg(feature = "drop-remove-path")]
9pub use self::drop_remove_path::DropRemovePath;
10
11#[cfg(feature = "download-to-path")]
12mod download_to_path;
13#[cfg(feature = "download-to-path")]
14pub use self::download_to_path::download_to_path;
15
16#[cfg(feature = "arc-anyhow-error")]
17mod arc_anyhow_error;
18#[cfg(feature = "arc-anyhow-error")]
19pub use self::arc_anyhow_error::ArcAnyhowError;
20
21use std::mem::ManuallyDrop;
22use std::ops::Deref;
23use std::path::Path;
24use std::path::PathBuf;
25
26pub fn try_create_dir<P>(path: P) -> std::io::Result<bool>
33where
34 P: AsRef<Path>,
35{
36 match std::fs::create_dir(path) {
37 Ok(()) => Ok(true),
38 Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => Ok(false),
39 Err(error) => Err(error),
40 }
41}
42
43pub fn try_remove_dir<P>(path: P) -> std::io::Result<bool>
50where
51 P: AsRef<Path>,
52{
53 match std::fs::remove_dir(path) {
54 Ok(()) => Ok(true),
55 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
56 Err(error) => Err(error),
57 }
58}
59
60#[derive(Debug)]
64pub struct DropRemovePathBlocking {
65 path: PathBuf,
67
68 should_remove: bool,
70}
71
72impl DropRemovePathBlocking {
73 pub fn new<P>(path: P) -> Self
75 where
76 P: AsRef<Path>,
77 {
78 Self {
79 path: path.as_ref().into(),
80 should_remove: true,
81 }
82 }
83
84 pub fn persist(&mut self) {
86 self.should_remove = false;
87 }
88
89 pub fn try_drop(self) -> Result<bool, (Self, std::io::Error)> {
96 let wrapper = ManuallyDrop::new(self);
97 let should_remove = wrapper.should_remove;
98
99 if should_remove {
100 std::fs::remove_file(&wrapper.path)
101 .map_err(|e| (ManuallyDrop::into_inner(wrapper), e))?;
102 }
103
104 Ok(should_remove)
105 }
106}
107
108impl AsRef<Path> for DropRemovePathBlocking {
109 fn as_ref(&self) -> &Path {
110 self.path.as_ref()
111 }
112}
113
114impl Deref for DropRemovePathBlocking {
115 type Target = Path;
116
117 fn deref(&self) -> &Self::Target {
118 &self.path
119 }
120}
121
122impl Drop for DropRemovePathBlocking {
123 fn drop(&mut self) {
124 if self.should_remove {
126 if let Err(error) = std::fs::remove_file(self.path.clone()) {
127 let message = format!("failed to delete file: '{error}'");
128 if std::thread::panicking() {
129 eprintln!("{message}");
130 } else {
131 panic!("{message}");
132 }
133 }
134 }
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use super::*;
141 use std::io::Write;
142
143 #[test]
144 fn try_create_dir_works() {
145 let path = "test_tmp/try_create_dir";
146
147 std::fs::create_dir_all("test_tmp").expect("failed to create tmp dir");
148
149 try_remove_dir(path).expect("failed to remove dir");
150 assert!(try_create_dir(path).expect("failed to create dir"));
151 assert!(!try_create_dir(path).expect("failed to create dir"));
152 assert!(try_remove_dir(path).expect("failed to remove dir"));
153 assert!(!try_remove_dir(path).expect("failed to remove dir"));
154 }
155
156 #[test]
157 fn drop_remove_file_blocking_sanity_check() {
158 std::fs::create_dir_all("test_tmp").expect("failed to create tmp dir");
159
160 let file_path: &Path = "test_tmp/drop_remove_file_blocking.txt".as_ref();
161 let file_data = b"testing 1 2 3";
162
163 {
164 let mut file = std::fs::File::create(file_path).expect("failed to create file");
165 let drop_remove_path = DropRemovePathBlocking::new(file_path);
166
167 file.write_all(file_data).expect("failed to write data");
168
169 drop(file);
170 drop_remove_path.try_drop().expect("failed to close file");
171 }
172 let exists = file_path.exists();
173 assert!(!exists, "nonpersisted file exists");
174
175 {
176 let mut file = std::fs::File::create(file_path).expect("failed to create file");
177 let mut drop_remove_path = DropRemovePathBlocking::new(file_path);
178
179 file.write_all(file_data).expect("failed to write data");
180
181 drop_remove_path.persist();
182
183 drop(file);
184 drop_remove_path.try_drop().expect("failed to close file");
185 }
186
187 let exists = file_path.exists();
188 assert!(exists, "persisted file does not exist");
189
190 let _ = std::fs::remove_file(file_path).is_ok();
192 }
193}