1use std::{
2 fs::{self, File},
3 io::{self, BufWriter, Seek, Write},
4 path::{Path, PathBuf},
5};
6use trash::{os_limited, TrashItem};
7
8use crate::{FileErr, RecursiveOperation};
9
10pub fn path_to_string<P: AsRef<Path>>(path: P) -> String {
14 match path.as_ref().to_str() {
15 Some(s) => s.to_string(),
16 None => path.as_ref().to_string_lossy().to_string(),
17 }
18}
19
20pub fn run_op_on_dir_recursive<T>(
21 operation: &mut T,
22 dir: &Path,
23 mut count: usize,
24) -> Result<usize, FileErr>
25where
26 T: RecursiveOperation,
27{
28 if dir.is_dir() {
29 for entry in fs::read_dir(dir).map_err(|e| FileErr::map(e, dir))? {
30 let entry = entry.map_err(|e| FileErr::map(e, dir))?;
31 let path = entry.path();
32 if path.is_dir() {
33 run_op_on_dir_recursive(operation, &path, count)?;
34 } else {
35 count += 1;
36 operation.display_cb(&path, false);
37 operation.cb(&path)?;
38 }
39 }
40 count += 1;
41 operation.display_cb(&PathBuf::from(dir), true);
42 operation.cb(&PathBuf::from(dir))?;
43 }
44 Ok(count)
45}
46
47pub fn select_from_trash(name: &String) -> Option<Vec<TrashItem>> {
48 let mut items: Vec<TrashItem> = Vec::new();
49
50 for item in os_limited::list().unwrap() {
51 if name == &item.name {
52 items.push(item);
53 }
54 }
55
56 if items.is_empty() {
57 return None;
58 }
59 Some(items)
60}
61
62pub fn get_existent_trash_items(
63 names: &Vec<String>,
64 s_cb: impl Fn(Vec<TrashItem>) -> TrashItem,
65 d_cb: impl Fn(String),
66) -> Vec<TrashItem> {
67 names
68 .iter()
69 .filter_map(|n| match select_from_trash(n) {
70 Some(i) => Some(s_cb(i)),
71 None => None,
72 })
73 .collect()
74}
75
76pub fn overwrite_file(file: &File, runs: usize) -> std::io::Result<()> {
77 const OW_BUFF_SIZE: usize = 10usize.pow(6);
78 let file_len = file.metadata()?.len();
79
80 if file.metadata()?.is_dir() {
81 return Ok(());
82 }
83
84 let mut writer = BufWriter::new(file);
85
86 let buf = vec![0u8; OW_BUFF_SIZE];
87
88 for _ in 0..runs {
89 writer.seek(io::SeekFrom::Start(0))?;
90
91 loop {
94 let offset = writer.seek(io::SeekFrom::Current(0)).unwrap();
95 if (file_len - offset) >= OW_BUFF_SIZE.try_into().unwrap() {
96 writer.write_all(&buf)?;
97 } else {
98 writer.write_all(&vec![0u8; (file_len - offset).try_into().unwrap()])?;
99 break;
100 }
101 }
102 }
103
104 Ok(())
105}
106
107pub fn remove_file_or_dir(path: &PathBuf) -> std::io::Result<()> {
108 if !path.exists() {
109 return Err(std::io::ErrorKind::NotFound.into());
110 }
111
112 if path.is_dir() {
113 return fs::remove_dir(path);
114 }
115 fs::remove_file(path)
116}
117
118pub fn get_existent_paths<'a, T, U>(input_paths: &'a T, d_cb: impl Fn(U)) -> Vec<U>
119where
120 &'a T: IntoIterator<Item = U>,
121 U: AsRef<Path> + 'a,
122{
123 input_paths
124 .into_iter()
125 .filter_map(|p| {
126 if p.as_ref().exists() {
127 Some(p)
128 } else {
129 d_cb(p);
130 return None;
131 }
132 })
133 .collect()
134}
135
136pub fn path_vec_from_string_vec<'a>(strings: Vec<&'a String>) -> Vec<&'a Path> {
138 let mut ret_vec = Vec::<&Path>::new();
139 for s in strings {
140 ret_vec.push(Path::new(s));
141 }
142 return ret_vec;
143}
144
145#[cfg(test)]
146mod tests {
147 use rand::distributions::{Alphanumeric, DistString};
148 use std::{fs::OpenOptions, io::Read};
149
150 use super::*;
151 #[test]
152 fn test_select_from_trash_exists_single() {
153 let filename = generate_random_filename();
154
155 File::create(&filename).unwrap();
156 trash::delete(&filename).unwrap();
157
158 let selected = select_from_trash(&filename);
159
160 assert!(selected.is_some());
161 let selected_val = selected.unwrap();
162
163 assert!(selected_val.len() == 1);
164
165 os_limited::purge_all([&(selected_val[0])]).unwrap();
166 }
167
168 #[test]
169 fn test_select_from_trash_exists_multiple() {
170 let filename = generate_random_filename();
171
172 File::create(&filename).unwrap();
173 trash::delete(&filename).unwrap();
174
175 File::create(&filename).unwrap();
176 trash::delete(&filename).unwrap();
177
178 let selected = select_from_trash(&filename);
179
180 assert!(selected.is_some());
181 let selected_val = selected.unwrap();
182 assert!(selected_val.len() == 2);
183
184 os_limited::purge_all(selected_val).unwrap();
185 }
186
187 #[test]
188 fn test_select_from_trash_fails() {
189 assert!(select_from_trash(&generate_random_filename()).is_none());
190 }
191
192 fn is_file_of_single_byte(mut file: &File, byte: u8) -> bool {
193 let file_len: usize = file.metadata().unwrap().len().try_into().unwrap();
194 let mut buf = Vec::<u8>::with_capacity(file_len);
195 file.seek(io::SeekFrom::Start(0)).unwrap();
196 file.read_to_end(&mut buf).unwrap();
197
198 if buf != vec![byte; file_len] {
199 println!("{}/{}", buf.len(), file_len);
200 return false;
201 }
202 true
203 }
204
205 #[test]
206 fn test_is_file_of_single_byte() {
207 let filename = generate_random_filename();
209
210 let mut file = OpenOptions::new()
211 .write(true)
212 .create(true)
213 .read(true)
214 .open(&filename)
215 .unwrap();
216
217 let ones = vec![1u8; 1024 ^ 2];
219 file.write_all(&ones).unwrap();
220 file.flush().unwrap();
221
222 if !is_file_of_single_byte(&file, 1u8) {
223 fs::remove_file(&filename).unwrap();
224
225 panic!()
226 } else {
227 fs::remove_file(&filename).unwrap();
228 }
229 }
230
231 #[test]
232 fn test_overwrite_file() {
233 let filename = generate_random_filename();
234
235 let mut file = OpenOptions::new()
236 .write(true)
237 .create(true)
238 .read(true)
239 .open(&filename)
240 .unwrap();
241
242 let ones = vec![1u8; 10usize.pow(6)];
244 file.write_all(&ones).unwrap();
245 file.flush().unwrap();
246
247 overwrite_file(&file, 1).unwrap();
248
249 if !is_file_of_single_byte(&file, 0u8) {
250 fs::remove_file(&filename).unwrap();
251 panic!();
252 } else {
253 fs::remove_file(&filename).unwrap();
254 }
255 }
256
257 fn generate_random_filename() -> String {
258 Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
259 + "."
260 + &Alphanumeric.sample_string(&mut rand::thread_rng(), 3)
261 }
262}