flatten_directory/
lib.rs

1use std::fs;
2use std::path::PathBuf;
3use walkdir::WalkDir;
4
5#[derive(Debug, Clone)]
6pub struct FlattenDirectory {
7    root: PathBuf,
8}
9
10impl FlattenDirectory {
11    pub fn new(root: PathBuf) -> Self {
12        Self { root }
13    }
14
15    pub fn execute(&self) -> Result<(), std::io::Error> {
16        self.flatten()?;
17        self.remove_directories(&self.root)?;
18
19        Ok(())
20    }
21
22    fn flatten(&self) -> Result<(), std::io::Error> {
23        let files = WalkDir::new(&self.root)
24            .into_iter()
25            .filter_map(|e| e.ok())
26            .filter(|e| e.file_type().is_file());
27
28        for f in files {
29            let mut dest = self.root.join(f.file_name());
30            // handle duplicated names
31            if dest.exists() {
32                let mut c = 0u128;
33                let p = f.path();
34                let filename = p.file_stem().unwrap_or_default().to_string_lossy();
35                let ext = p.extension().unwrap_or_default().to_string_lossy();
36                let dot = if ext == "" { "" } else { "." };
37                loop {
38                    dest = self.root.join(format!("{filename}_{c}{dot}{ext}"));
39                    if !dest.exists() {
40                        break;
41                    }
42                    c += 1;
43                }
44            }
45
46            fs::rename(f.path(), dest)?;
47        }
48
49        Ok(())
50    }
51
52    fn remove_directories(&self, root: &PathBuf) -> Result<(), std::io::Error> {
53        for path in fs::read_dir(root)?
54            .into_iter()
55            .filter_map(|f| f.ok())
56            .filter(|f| f.path().is_dir())
57            .map(|f: fs::DirEntry| f.path())
58        {
59            self.remove_directories(&path)?;
60            fs::remove_dir(path)?;
61        }
62
63        Ok(())
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use fake::{Fake, Faker};
71
72    #[test]
73    fn test_default() {
74        let path = Faker.fake::<PathBuf>();
75        let fd = FlattenDirectory::new(path.clone());
76        assert_eq!(fd.root, path);
77    }
78}