1pub mod filter;
2pub mod remover;
3
4use crate::filter::{Filter, FilterEngine};
5use crate::remover::MockRemover;
6pub use crate::remover::RealRemover;
7use clap::Parser;
8use remover::Remover;
9use std::error::Error;
10use std::path::PathBuf;
11use walkdir::DirEntry;
12use walkdir::WalkDir;
13
14#[derive(Parser)]
15pub struct Cli {
16 #[arg(short = 'd')]
18 pub dir: String,
19 #[arg(long = "fd")]
21 pub filter_dir: Option<Vec<String>>,
22 #[arg(long = "ext")]
24 pub file_ext: Option<Vec<String>>,
25 #[arg(long = "day")]
27 pub day: Option<i32>,
28 #[arg(long = "size")]
30 pub size: Option<String>,
31 #[arg(long = "skip_dir")]
33 pub is_skip_dir: bool,
34 #[arg(long = "is_test")]
36 pub is_test: bool,
37}
38
39pub fn run(args: &Cli) -> Result<Vec<PathBuf>, Box<dyn Error>> {
41 if args.is_test{
42 let mut mock_remover = MockRemover::new();
43 remove_files(args, &mut mock_remover)?;
44 Ok(mock_remover.removed.borrow().to_vec())
45 }else{
46 let mut real_remover = RealRemover {
47 is_skip_dir: args.is_skip_dir.clone(),
48 };
49 remove_files(args, &mut real_remover)?;
50 Ok(Vec::new())
51 }
52}
53
54pub fn remove_files<T:Remover>(args: &Cli, remover: &mut T) -> Result<(), Box<dyn Error>> {
55 let filter_engine = FilterEngine::from(&args)?;
56 let skip_dirs = args.filter_dir.clone().unwrap_or_default();
57 for entry in WalkDir::new(&args.dir)
58 .into_iter()
59 .filter_entry(|e| is_not_target_dir(e, &skip_dirs))
60 .filter_map(Result::ok)
61 .skip(1) {
63 if let Ok(_) = filter_engine.filter(&entry) {
64 remover.remove(entry.path())?;
65 }
66 }
67 Ok(())
68}
69
70fn is_not_target_dir(entry: &DirEntry, skip_dirs: &Vec<String>) -> bool {
72 if skip_dirs.is_empty() {
73 return true;
74 }
75 if entry.file_type().is_dir() {
76 if let Some(name) = entry.file_name().to_str() {
77 return !skip_dirs.contains(&name.to_string());
78 }
79 }
80 true
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use std::fs::{create_dir, File};
87 use tempfile::tempdir;
88
89 fn make_cli(dir: &str) -> Cli {
91 Cli {
92 dir: dir.to_string(),
93 filter_dir: None,
94 file_ext: None,
95 day: None,
96 size: None,
97 is_skip_dir: false,
98 is_test: true
99 }
100 }
101
102 #[test]
103 fn test_remove_all_files() {
104 let temp = tempdir().unwrap();
105 let file1 = temp.path().join("a.txt");
106 let file2 = temp.path().join("b.txt");
107
108 File::create(&file1).unwrap();
109 File::create(&file2).unwrap();
110
111 let cli = make_cli(temp.path().to_str().unwrap());
112
113 let ret = run(&cli).unwrap();
114 assert_eq!(ret.len(), 2);
115 }
116
117 #[test]
118 fn test_skip_specific_dir() {
119 let temp = tempdir().unwrap();
120
121 let skip_dir = temp.path().join("skip_me");
122 let normal_dir = temp.path().join("delete_me");
123
124 create_dir(&skip_dir).unwrap();
125 create_dir(&normal_dir).unwrap();
126
127 let file1 = skip_dir.join("a.txt");
128 let file2 = normal_dir.join("b.txt");
129
130 File::create(&file1).unwrap();
131 File::create(&file2).unwrap();
132
133 let cli = Cli {
134 dir: temp.path().to_str().unwrap().to_string(),
135 filter_dir: Some(vec!["skip_me".to_string()]),
136 file_ext: None,
137 day: None,
138 size: None,
139 is_skip_dir: false,
140 is_test: true
141 };
142
143 let ret = run(&cli).unwrap();
144 assert_eq!(ret.len(), 2);
146 }
149}