Skip to main content

pulith_fs/primitives/
replace_dir.rs

1use crate::{Error, Result};
2use std::path::Path;
3
4pub struct Options {
5    pub retry_count: u32,
6    pub retry_delay: std::time::Duration,
7}
8
9impl Default for Options {
10    fn default() -> Self {
11        Self {
12            retry_count: 5,
13            retry_delay: std::time::Duration::from_millis(100),
14        }
15    }
16}
17
18impl Options {
19    pub fn new() -> Self {
20        Self::default()
21    }
22    pub fn retry_count(mut self, count: u32) -> Self {
23        self.retry_count = count;
24        self
25    }
26    pub fn retry_delay(mut self, delay: std::time::Duration) -> Self {
27        self.retry_delay = delay;
28        self
29    }
30}
31
32pub fn replace_dir(src: impl AsRef<Path>, dest: impl AsRef<Path>, _options: Options) -> Result<()> {
33    let src = src.as_ref();
34    let dest = dest.as_ref();
35
36    #[cfg(unix)]
37    {
38        std::fs::rename(src, dest).map_err(|e| Error::ReplaceDir {
39            path: dest.to_path_buf(),
40            source: e,
41        })
42    }
43
44    #[cfg(windows)]
45    {
46        use std::thread;
47        let options = _options;
48        let mut attempts = 0;
49        loop {
50            if dest.exists()
51                && let Err(e) = std::fs::remove_dir_all(dest)
52            {
53                attempts += 1;
54                if attempts >= options.retry_count {
55                    return Err(Error::ReplaceDir {
56                        path: dest.to_path_buf(),
57                        source: e,
58                    });
59                }
60                thread::sleep(options.retry_delay * attempts);
61                continue;
62            }
63
64            match std::fs::rename(src, dest) {
65                Ok(_) => return Ok(()),
66                Err(e) => {
67                    attempts += 1;
68                    if attempts >= options.retry_count {
69                        return Err(Error::ReplaceDir {
70                            path: dest.to_path_buf(),
71                            source: e,
72                        });
73                    }
74                    thread::sleep(options.retry_delay * attempts);
75                }
76            }
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use tempfile::tempdir;
85
86    #[test]
87    fn test_replace_dir() {
88        let dir = tempdir().unwrap();
89        let src = dir.path().join("src");
90        let dest = dir.path().join("dest");
91        std::fs::create_dir_all(&src).unwrap();
92        std::fs::write(src.join("file.txt"), "data").unwrap();
93
94        replace_dir(&src, &dest, Options::new()).unwrap();
95        assert!(dest.exists());
96        assert!(dest.join("file.txt").exists());
97    }
98}