1use std::fs::Metadata;
3use std::path::Path;
4use std::{fs, io};
5
6#[cfg(test)]
7mod tests;
8
9pub fn ignore_not_found<Op>(mut op: Op) -> io::Result<()>
12where
13 Op: FnMut() -> io::Result<()>,
14{
15 match op() {
16 Ok(()) => Ok(()),
17 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
18 Err(e) => Err(e),
19 }
20}
21
22#[track_caller]
34pub fn recursive_remove<P: AsRef<Path>>(path: P) -> io::Result<()> {
35 let path = path.as_ref();
36
37 let metadata = match fs::symlink_metadata(path) {
41 Ok(m) => m,
42 Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
43 Err(e) => return Err(e),
44 };
45
46 #[cfg(windows)]
47 let is_dir_like = |meta: &fs::Metadata| {
48 use std::os::windows::fs::FileTypeExt;
49 meta.is_dir() || meta.file_type().is_symlink_dir()
50 };
51 #[cfg(not(windows))]
52 let is_dir_like = fs::Metadata::is_dir;
53
54 const MAX_RETRIES: usize = 5;
55 const RETRY_DELAY_MS: u64 = 100;
56
57 let try_remove = || {
58 if is_dir_like(&metadata) {
59 fs::remove_dir_all(path)
60 } else {
61 try_remove_op_set_perms(fs::remove_file, path, metadata.clone())
62 }
63 };
64
65 for attempt in 0..MAX_RETRIES {
71 match try_remove() {
72 Ok(()) => return Ok(()),
73 Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
74 Err(_) if attempt < MAX_RETRIES - 1 => {
75 std::thread::sleep(std::time::Duration::from_millis(RETRY_DELAY_MS));
76 continue;
77 }
78 Err(e) => return Err(e),
79 }
80 }
81
82 Ok(())
83}
84
85fn try_remove_op_set_perms<'p, Op>(mut op: Op, path: &'p Path, metadata: Metadata) -> io::Result<()>
86where
87 Op: FnMut(&'p Path) -> io::Result<()>,
88{
89 match op(path) {
90 Ok(()) => Ok(()),
91 Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
92 let mut perms = metadata.permissions();
93 perms.set_readonly(false);
94 fs::set_permissions(path, perms)?;
95 op(path)
96 }
97 Err(e) => Err(e),
98 }
99}
100
101pub fn remove_and_create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
102 let path = path.as_ref();
103 recursive_remove(path)?;
104 fs::create_dir_all(path)
105}