1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::ffi::OsString;
use std::fs::{read_dir, DirEntry, File};
use std::path::{Path, PathBuf};

pub trait ForeachDir {
    /// Traversal a directory recursively
    ///
    /// # Examples
    /// ```no_run
    /// use std::path::Path;
    /// use bczhc_lib::fs::ForeachDir;
    ///
    /// let path = Path::new("/tmp/zhc");
    /// path.traversal_dir(|entry| {
    ///     println!("{:?}", entry);
    /// });
    /// ```
    fn traversal_dir<F>(&self, callback: F) -> std::io::Result<()>
    where
        F: Fn(std::io::Result<&DirEntry>) + Clone;
}

fn recursive_traversal_dir<T, F>(path: &T, callback: F) -> std::io::Result<()>
where
    T: AsRef<Path> + ?Sized,
    F: Fn(std::io::Result<&DirEntry>) + Clone,
{
    let dir = read_dir(path)?;
    for entry in dir {
        let entry = entry;
        if let Ok(ref d) = entry {
            callback(Ok(d));
            let file_type = d.file_type();
            if let Ok(t) = file_type {
                if t.is_dir() {
                    recursive_traversal_dir(&d.path(), callback.clone())?;
                }
            } else if let Err(e) = file_type {
                callback(Err(e));
            }
        } else {
            callback(Ok(&entry.unwrap()));
        }
    }

    Ok(())
}

impl ForeachDir for Path {
    fn traversal_dir<F>(&self, callback: F) -> std::io::Result<()>
    where
        F: Fn(std::io::Result<&DirEntry>) + Clone,
    {
        recursive_traversal_dir(self, callback)
    }
}

/// create a file with unique filename for preventing from overwriting existing files
///
/// # Examples
/// ```no_run
/// use std::path::Path;
/// use bczhc_lib::fs::new_unique_file;
///
/// let path = Path::new("~/hello");
/// // now `path` doesn't exist; return without any changes
/// assert_eq!(new_unique_file(path).unwrap().as_path(), path);
///
/// let new_path = new_unique_file(path);
/// // the new path contains ".1" suffix
/// assert_eq!(new_path.unwrap().as_path(), Path::new("~/hello.1"))
/// ```
pub fn new_unique_file<P>(path: P) -> std::io::Result<PathBuf>
where
    P: AsRef<Path>,
{
    let path = path.as_ref();
    if !path.exists() {
        File::create(path)?;
        return Ok(path.into());
    }

    let mut counter = 1;
    loop {
        let mut string = OsString::from(path.as_os_str());
        string.push(format!(".{}", counter));
        let new_path = PathBuf::from(&string);
        if !new_path.exists() {
            File::create(&new_path)?;
            return Ok(new_path);
        }
        counter += 1;
    }
}