use crate::{
predicates::{
languages::{
git::GitDirtyRepoPredicate, node::NodeModulesPredicate, rust::RustTargetPredicate,
},
stop::{HiddenDirStop, IsFileStop, Stop},
Reportable,
},
results::AnalyzeTarget,
};
use fs_extra::dir::get_size;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::path::Path;
pub struct Scanner {
depth: u16,
stop_conditions: Vec<Box<dyn Stop>>,
report_conditions: Vec<Box<dyn Reportable>>,
pb: Option<ProgressBar>,
}
impl Scanner {
pub fn new(
depth: u16,
stop_conditions: Vec<Box<dyn Stop>>,
report_conditions: Vec<Box<dyn Reportable>>,
cmd_progress_bar: bool,
) -> Self {
let pb = if cmd_progress_bar {
let pb = ProgressBar::new(1);
let spinner_style = ProgressStyle::with_template("{spinner} {wide_msg}").unwrap();
pb.set_style(spinner_style);
Some(pb)
} else {
None
};
Scanner {
depth,
stop_conditions,
report_conditions,
pb,
}
}
pub fn _scan_recursive(&mut self, path: &Path, depth: u16) -> Vec<AnalyzeTarget> {
if self.pb.is_some() && path.is_dir() {
self.pb
.as_ref()
.unwrap()
.set_message(format!("Scanning: {}", path.to_string_lossy()));
}
if depth > self.depth {
return vec![];
}
let mut targets = vec![];
for condition in &self.report_conditions {
if condition.report(path) {
targets.push(AnalyzeTarget::new(path.to_path_buf(), depth, None));
return targets;
}
}
for stop_condition in &self.stop_conditions {
if stop_condition.stop(path) {
return targets;
}
}
if path.is_file() {
return targets;
}
for entry in path.read_dir().unwrap() {
let entry = entry.unwrap();
targets.extend(self._scan_recursive(&entry.path(), depth + 1))
}
if depth == 0 {
self.pb
.as_ref()
.unwrap()
.finish_with_message("Scan Finished...");
}
targets
}
pub fn scan_recursive(&mut self, path: &Path, depth: u16) -> Vec<AnalyzeTarget> {
let mut targets = self._scan_recursive(path, depth);
targets.par_iter_mut().for_each(|target| {
target.size = Some(get_size(&target.path).unwrap_or(0));
});
targets
}
pub fn scan_parallel(&self, path: &Path, depth: u16) -> Vec<AnalyzeTarget> {
if self.pb.is_some() && path.is_dir() {
self.pb
.as_ref()
.unwrap()
.set_message(format!("Scanning: {}", path.to_string_lossy()));
}
if depth > self.depth {
return vec![];
}
let mut targets = vec![];
for condition in &self.report_conditions {
if condition.report(path) {
targets.push(AnalyzeTarget::new(
path.to_path_buf(),
depth,
Some(get_size(path).unwrap_or(0)),
));
return targets;
}
}
for stop_condition in &self.stop_conditions {
if stop_condition.stop(path) {
return targets;
}
}
if path.is_file() {
return targets;
}
let entries: Vec<_> = path
.read_dir()
.unwrap()
.filter_map(Result::ok)
.collect::<Vec<_>>();
let results: Vec<AnalyzeTarget> = entries
.par_iter()
.flat_map(|entry| {
let path = entry.path();
if path.is_dir() {
self.scan_parallel(&path, depth + 1)
} else {
vec![]
}
})
.collect();
targets.extend(results);
if depth == 0 && self.pb.is_some() {
self.pb
.as_ref()
.unwrap()
.finish_with_message("Scan Finished...");
}
targets
}
pub fn set_depth(&mut self, depth: u16) {
self.depth = depth;
}
}
pub fn get_dirty_git_repo_scanner(depth: u16, pb: bool) -> Scanner {
let stop_conditions: Vec<Box<dyn Stop>> =
vec![Box::new(IsFileStop {}), Box::new(HiddenDirStop {})];
let report_conditions: Vec<Box<dyn Reportable>> = vec![Box::new(GitDirtyRepoPredicate {})];
Scanner::new(
depth,
stop_conditions,
report_conditions,
pb,
)
}
pub fn get_project_garbage_scanner(depth: u16, pb: bool) -> Scanner {
let stop_conditions: Vec<Box<dyn Stop>> =
vec![Box::new(IsFileStop {}), Box::new(HiddenDirStop {})];
let report_conditions: Vec<Box<dyn Reportable>> = vec![
Box::new(RustTargetPredicate {}),
Box::new(NodeModulesPredicate {}),
];
Scanner::new(
depth,
stop_conditions,
report_conditions,
pb,
)
}