1pub mod file_criteria;
2mod file_utils;
3
4use file_criteria::FindCriteria;
5
6#[derive(Debug)]
7pub enum FindError {
8 InvalidCriteria(String),
9 NotFound(String),
10 NotADirectory(String),
11}
12impl std::fmt::Display for FindError {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 match self {
15 FindError::InvalidCriteria(e) => write!(f, "Invalid criteria: {}", e),
16 FindError::NotFound(p) => write!(f, "Path not found: {}", p),
17 FindError::NotADirectory(p) => write!(f, "Path is not a directory: {}", p),
18 }
19 }
20}
21impl std::error::Error for FindError {}
22
23pub fn find(path: &str, criteria: &FindCriteria) -> Result<Vec<String>, FindError> {
24 file_criteria::validate(criteria).map_err(|err| err)?;
25
26 let research_path = std::path::Path::new(path);
27
28 if !research_path.exists() {
29 return Err(FindError::NotFound(path.to_string()));
30 }
31
32 if !research_path.is_dir() {
33 return Err(FindError::NotADirectory(path.to_string()));
34 }
35
36 let mut found_files = Vec::new();
37
38 if let Ok(entries) = std::fs::read_dir(research_path) {
39 for entry_result in entries {
40 if let Ok(entry) = entry_result {
41 let file_path = entry.path();
42 if file_path.is_dir() && criteria.recursive {
43 if let Some(subdirectory_path) = file_path.to_str() {
44 if let Ok(subdirectory_files) = find(subdirectory_path, criteria) {
45 found_files.extend(subdirectory_files);
46 }
47 }
48 } else if file_path.is_file() && file_utils::accept(&entry, criteria) {
49 if let Some(file_str) = file_path.to_str() {
50 found_files.push(file_str.to_string());
51 }
52 }
53 }
54 }
55 }
56
57 Ok(found_files)
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use std::ffi::OsString;
64
65 #[test]
66 fn none_existing_path() {
67 let criteria = FindCriteria::new();
68 let result = find("tests_files/empty2", &criteria);
69 assert!(result.is_err());
70 assert_eq!(
71 result.unwrap_err().to_string(),
72 "Path not found: tests_files/empty2"
73 );
74 }
75
76 #[test]
77 fn path_is_a_file() {
78 let criteria = FindCriteria::new();
79 let result = find("tests_files/file", &criteria);
80 assert!(result.is_err());
81 assert_eq!(
82 result.unwrap_err().to_string(),
83 "Path is not a directory: tests_files/file"
84 );
85 }
86
87 #[test]
88 fn validate_file_size_min_greater_than_max() {
89 let criteria = FindCriteria::new()
90 .file_size_minimum(200)
91 .file_size_maximum(100);
92 let result = file_criteria::validate(&criteria);
93 assert!(result.is_err());
94 assert_eq!(
95 result.unwrap_err().to_string(),
96 "Invalid criteria: Minimum file size cannot be greater than maximum file size"
97 );
98 }
99
100 #[test]
101 fn all_files() {
102 let criteria = FindCriteria::new();
103 let result = find("tests_files", &criteria);
104 assert!(result.is_ok());
105 assert_eq!(result.unwrap().len(), 5);
106 }
107
108 #[test]
109 fn not_recursive_files() {
110 let criteria = FindCriteria::new().recursive(false);
111 let result = find("tests_files", &criteria);
112 assert!(result.is_ok());
113 assert_eq!(result.unwrap().len(), 3);
114 }
115
116 #[test]
117 fn search_name() {
118 let criteria = FindCriteria::new().file_name(OsString::from("f"));
119 let result = find("tests_files", &criteria);
120 assert!(result.is_ok());
121 assert_eq!(result.unwrap().len(), 2);
122 }
123
124 #[test]
125 fn search_png() {
126 let criteria = FindCriteria::new().file_extension(OsString::from("png"));
127 let result = find("tests_files", &criteria);
128 assert!(result.is_ok());
129 assert_eq!(result.unwrap().len(), 2);
130 }
131
132 #[test]
133 fn search_size_min() {
134 let criteria = FindCriteria::new().file_size_minimum(89534);
135 let result = find("tests_files", &criteria);
136 assert!(result.is_ok());
137 assert_eq!(result.unwrap().len(), 2);
138 }
139
140 #[test]
141 fn search_size_max() {
142 let criteria = FindCriteria::new().file_size_maximum(89534);
143 let result = find("tests_files", &criteria);
144 assert!(result.is_ok());
145 assert_eq!(result.unwrap().len(), 4);
146 }
147
148 #[test]
149 fn search_multiple_criteria() {
150 let criteria = FindCriteria::new()
151 .file_name(OsString::from("image"))
152 .file_extension(OsString::from("png"))
153 .file_size_minimum(89)
154 .file_size_maximum(100000);
155 let result = find("tests_files", &criteria);
156 assert!(result.is_ok());
157 assert_eq!(result.unwrap().len(), 1);
158 }
159}