diskforge_core/
scanner.rs1use std::path::{Path, PathBuf};
2
3use anyhow::Result;
4use rayon::prelude::*;
5
6use crate::disk;
7use crate::rules::{
8 android, dev_artifacts, docker, known_paths, simulator, system_junk, user_rules,
9};
10use crate::safety;
11use crate::sizing;
12use crate::types::ScanResult;
13
14pub struct ScanOptions {
16 pub min_size: u64,
18 pub older_than: Option<u64>,
20 pub project_dirs: Vec<PathBuf>,
22}
23
24impl Default for ScanOptions {
25 fn default() -> Self {
26 let home = std::env::var("HOME").unwrap_or_else(|_| "/Users".into());
27 Self {
28 min_size: 1024 * 1024, older_than: None,
30 project_dirs: vec![PathBuf::from(&home)],
31 }
32 }
33}
34
35pub fn scan(options: &ScanOptions) -> Result<ScanResult> {
37 let home = std::env::var("HOME")?;
38 let home_path = Path::new(&home);
39
40 let disk_info = disk::get_disk_info(home_path)?;
42
43 let mut items = Vec::new();
44
45 let rules = known_paths::all_known_path_rules(&home);
47 let known_items: Vec<_> = rules
48 .into_par_iter()
49 .filter(|rule| rule.path.exists())
50 .filter_map(|rule| {
51 let stats = sizing::dir_stats(&rule.path);
52 if stats.size >= options.min_size {
53 Some(rule.into_item_with_stats(stats.size, stats.last_modified))
54 } else {
55 None
56 }
57 })
58 .collect();
59 items.extend(known_items);
60
61 for project_dir in &options.project_dirs {
63 if project_dir.exists() {
64 let dev_items = dev_artifacts::scan_dev_artifacts(project_dir);
65 items.extend(dev_items);
66 }
67 }
68
69 items.extend(simulator::scan_simulators(&home));
71
72 items.extend(android::scan_android(&home));
74
75 items.extend(docker::scan_docker(&home));
77
78 items.extend(user_rules::scan_user_rules(&home));
80
81 items.extend(system_junk::scan_system_junk(&home));
83
84 if let Some(older_than_secs) = options.older_than {
86 let now = std::time::SystemTime::now();
87 items.retain(|item| {
88 item.last_modified
89 .and_then(|lm| now.duration_since(lm).ok())
90 .is_some_and(|age| age.as_secs() >= older_than_secs)
91 });
92 }
93
94 for item in &mut items {
96 item.risk = safety::adjust_risk(&item.path, item.risk);
97 }
98
99 let mut result = ScanResult {
100 items,
101 disk_total: disk_info.total,
102 disk_used: disk_info.used,
103 disk_free: disk_info.free,
104 };
105
106 result.sort_by_size();
107 Ok(result)
108}