1use crate::structs::Dir;
2use log::{debug, warn};
3use rayon::prelude::*;
4use std::fs;
5use std::io::ErrorKind;
6use std::path::{Path, PathBuf};
7use std::time::{Duration, Instant};
8
9pub fn make_dir_tree(path_to_dir: PathBuf) -> Dir {
28 let mut contents = Vec::new();
29 let r_dir = match fs::read_dir(&path_to_dir) {
30 Ok(dir) => dir,
31 Err(err) => {
32 warn!(
33 "Error occured when trying to read {} error: {}",
34 path_to_dir.display(),
35 err
36 );
37 return Dir::new(0, path_to_dir, None, false);
38 }
39 };
40
41 for entry in r_dir {
42 let entry = entry.unwrap();
43 let path = entry.path();
44
45 if path.is_dir() {
46 debug!("{} is a directory", path.display());
47 contents.push(make_dir_tree(path));
48 } else {
49 let size = match fs::metadata(&path) {
50 Ok(file) => file.len(),
51 Err(err) => match err.kind() {
52 ErrorKind::PermissionDenied => {
53 warn!(
54 "Permission denied when accesing file/directory: {}",
55 path.display()
56 );
57 continue;
58 }
59 _ => {
60 warn!(
61 "Error occured when trying to read {} error: {}",
62 path.display(),
63 err
64 );
65 continue;
66 }
67 },
68 };
69 debug!("{} is a file with size: {} bytes", path.display(), size);
70 let is_file = path.is_file();
71 contents.push(Dir::new(size, path, None, is_file));
72 }
73 }
74 let sizes: Vec<u64> = contents.iter().map(|x: &Dir| x.size).collect();
75 let dir = Dir::new(sizes.iter().sum(), path_to_dir, Some(contents), false);
76 dir
77}
78
79pub fn make_dir_tree_parallel(path_to_dir: PathBuf) -> Dir {
80 let r_dir = match fs::read_dir(&path_to_dir) {
81 Ok(dir) => dir,
82 Err(err) => {
83 warn!(
84 "Error occured when trying to read {} error: {}",
85 path_to_dir.display(),
86 err
87 );
88 return Dir::new(0, path_to_dir, None, false);
89 }
90 };
91 let contents = r_dir
92 .into_iter()
93 .par_bridge()
94 .filter_map(|entry| {
95 let entry = entry.unwrap();
96 let path = entry.path();
97
98 if path.is_dir() {
99 debug!("{} is a directory", path.display());
100 Some(make_dir_tree(path))
101 } else {
102 let size = match fs::metadata(&path) {
103 Ok(file) => file.len(),
104 Err(err) => match err.kind() {
105 ErrorKind::PermissionDenied => {
106 warn!(
107 "Permission denied when accesing file/directory: {}",
108 path.display()
109 );
110 return None;
111 }
112 _ => {
113 warn!(
114 "Error occured when trying to read {} error: {}",
115 path.display(),
116 err
117 );
118 return None;
119 }
120 },
121 };
122 debug!("{} is a file with size: {} bytes", path.display(), size);
123 let is_file = path.is_file();
124 Some(Dir::new(size, path, None, is_file))
125 }
126 })
127 .collect::<Vec<Dir>>();
128
129 let sizes: Vec<u64> = contents.iter().map(|x: &Dir| x.size).collect();
130 let dir = Dir::new(sizes.iter().sum(), path_to_dir, Some(contents), false);
131 dir
132}
133
134fn _benchmark_make_dir_tree(func: fn(PathBuf) -> Dir, n: i32) -> f32 {
135 let mut times: Vec<Duration> = vec![];
136 for _i in 0..n {
137 let start = Instant::now();
138 let root = Path::new(r".");
139 let _result = func(root.to_path_buf());
140 let end = Instant::now();
141 let time = end - start;
142 times.push(time)
143 }
144 let total_time = times
145 .iter()
146 .fold(Duration::default(), |acc, &x| acc + x)
147 .as_secs_f32();
148 let average_time: f32 = total_time / times.len() as f32;
149
150 println!(
151 "function ran {} times and took an average of {} seconds, total: {} seconds",
152 n, average_time, total_time
153 );
154 average_time
155}
156
157#[cfg(test)]
158mod bench {
159 use crate::scanning::{_benchmark_make_dir_tree, make_dir_tree, make_dir_tree_parallel};
160
161 #[test]
162 #[ignore]
163 fn benchmark_make_dir_tree() {
164 _benchmark_make_dir_tree(make_dir_tree, 10);
165 }
166
167 #[test]
168 #[ignore]
169 fn benchmark_make_dir_tree_parallel() {
170 _benchmark_make_dir_tree(make_dir_tree_parallel, 10);
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use crate::scanning::{make_dir_tree, make_dir_tree_parallel};
177 use std::path::Path;
178
179 #[test]
180 fn test_make_dir_tree() {
181 let root = Path::new(r".");
182 let tree = make_dir_tree(root.to_path_buf());
183 for sub_dir in tree.contents.unwrap().iter() {
184 println!("{}", sub_dir.display_default())
185 }
186 }
187
188 #[test]
189 fn test_make_dir_tree_parallel() {
190 let root = Path::new(r".");
191 let tree = make_dir_tree_parallel(root.to_path_buf());
192 for sub_dir in tree.contents.unwrap().iter() {
193 println!("{}", sub_dir.display_default())
194 }
195 }
196}