Skip to main content

mimick/
cache_manager.rs

1//! Unified prune + clear over every on-disk cache subdirectory.
2
3use std::path::PathBuf;
4use std::time::SystemTime;
5
6/// Cache subdirectories under `profile::cache_dir()`. Append to extend.
7pub const CACHE_SUBDIRS: &[&str] = &[
8    "thumbnails",
9    "raw_decode",
10    "exif",
11    "video",
12    "preview",
13    "open-in",
14    "drag_export",
15];
16
17/// Yield to the scheduler every N file ops during a prune sweep.
18const YIELD_EVERY: usize = 64;
19
20fn cache_root() -> Option<PathBuf> {
21    crate::profile::cache_dir()
22}
23
24/// Remove every managed cache subdir. Call from `spawn_blocking`.
25pub fn clear_all_blocking() -> Result<(), String> {
26    let Some(root) = cache_root() else {
27        return Ok(());
28    };
29    let mut first_error: Option<String> = None;
30    for sub in CACHE_SUBDIRS {
31        let dir = root.join(sub);
32        if !dir.exists() {
33            continue;
34        }
35        if let Err(err) = std::fs::remove_dir_all(&dir)
36            && first_error.is_none()
37        {
38            first_error = Some(format!("{}: {}", dir.display(), err));
39        }
40    }
41    match first_error {
42        Some(err) => Err(err),
43        None => Ok(()),
44    }
45}
46
47/// LRU-evict files across all subdirs until total size ≤ `cap_bytes`.
48pub fn prune_all_blocking(cap_bytes: u64) {
49    let Some(root) = cache_root() else {
50        return;
51    };
52    let mut entries: Vec<(PathBuf, u64, SystemTime)> = Vec::new();
53    let mut total: u64 = 0;
54    for sub in CACHE_SUBDIRS {
55        collect_files(&root.join(sub), &mut entries, &mut total);
56    }
57    if total <= cap_bytes {
58        return;
59    }
60    entries.sort_by_key(|(_, _, mtime)| *mtime);
61    let mut count: usize = 0;
62    for (path, size, _) in entries {
63        if total <= cap_bytes {
64            break;
65        }
66        if std::fs::remove_file(&path).is_ok() {
67            total = total.saturating_sub(size);
68        }
69        count += 1;
70        if count.is_multiple_of(YIELD_EVERY) {
71            std::thread::yield_now();
72        }
73    }
74}
75
76fn collect_files(
77    dir: &std::path::Path,
78    out: &mut Vec<(PathBuf, u64, SystemTime)>,
79    total: &mut u64,
80) {
81    let Ok(read) = std::fs::read_dir(dir) else {
82        return;
83    };
84    for entry in read.flatten() {
85        let path = entry.path();
86        let Ok(metadata) = entry.metadata() else {
87            continue;
88        };
89        if metadata.is_dir() {
90            collect_files(&path, out, total);
91            continue;
92        }
93        let size = metadata.len();
94        let mtime = metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH);
95        *total = total.saturating_add(size);
96        out.push((path, size, mtime));
97    }
98}