#[macro_export]
macro_rules! roots {
(@build [$($acc:tt)*]) => { &[ $($acc)* ] };
(@build [$($acc:tt)*]
#[doc = $_a:literal] #[doc = $_b:literal] $path:literal $(, $($rest:tt)*)?
) => {
compile_error!(concat!(
"putzen: cache root \"", $path, "\" has multiple `///` lines. ",
"Use exactly one — extend prose with plain `//` instead."
));
};
(@build [$($acc:tt)*]
#[doc = $label:literal] $path:literal $(, $($rest:tt)*)?
) => {
roots!(@build [
$($acc)*
$crate::caches::defaults::DefaultRoot {
label: $crate::caches::defaults::strip_leading_spaces($label),
path: $path,
},
] $($($rest)*)?)
};
(@build [$($acc:tt)*] $path:literal $(, $($rest:tt)*)?) => {
compile_error!(concat!(
"putzen: cache root \"", $path, "\" is missing its `///` label. ",
"Add a one-line `///` doc comment above this entry."
));
};
( $($t:tt)* ) => { roots!(@build [] $($t)*) };
}
pub struct DefaultRoot {
pub label: &'static str,
pub path: &'static str, }
pub const fn strip_leading_spaces(s: &'static str) -> &'static str {
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() && bytes[i] == b' ' {
i += 1;
}
s.split_at(i).1
}
pub const fn _missing_doc_check() {}
pub const fn _multi_doc_check() {}
pub const SEEDS: &[DefaultRoot] = roots![
".cargo/registry/cache",
".cargo/registry/src",
".cargo/registry/index",
".cargo/git/checkouts",
".cargo/git/db",
"go/pkg/mod",
".m2/repository",
".gradle/caches",
".gradle/wrapper/dists",
".ivy2/cache",
".sbt/boot",
".nuget/packages",
".hex/packages",
".opam/download-cache",
".gitlibs",
".cabal/packages",
".ollama/models",
".triton/cache",
".nv/ComputeCache",
];
#[cfg(target_family = "unix")]
pub const SEEDS_OS: &[DefaultRoot] = roots![
".cache",
"Library/Caches",
"Library/Developer/Xcode/DerivedData",
"Library/Developer/Xcode/Archives",
"Library/Developer/Xcode/iOS DeviceSupport",
"Library/Developer/CoreSimulator/Caches",
".npm",
".yarn/cache",
".bun/install/cache",
".pnpm-store",
".cache/sccache",
"Library/Caches/Mozilla.sccache",
];
#[cfg(target_family = "windows")]
pub const SEEDS_OS: &[DefaultRoot] = roots![
"AppData/Local/Microsoft/Windows/INetCache",
"AppData/Local/Microsoft/Edge/User Data/Default/Cache",
"AppData/Local/Google/Chrome/User Data/Default/Cache",
"AppData/Roaming/npm-cache",
"AppData/Local/Yarn/Cache",
"AppData/Local/pnpm",
"AppData/Local/pip/Cache",
"AppData/Local/uv/cache",
"AppData/Local/huggingface",
"AppData/Local/go-build",
"AppData/Local/JetBrains",
"AppData/Roaming/Code/CachedData",
"AppData/Local/Mozilla/sccache",
];
pub fn defaults() -> impl Iterator<Item = &'static DefaultRoot> {
SEEDS.iter().chain(SEEDS_OS.iter())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn macro_emits_label_and_path() {
const SAMPLE: &[DefaultRoot] = roots![
".cargo/registry/cache",
];
assert_eq!(SAMPLE.len(), 1);
assert_eq!(SAMPLE[0].label, "cargo registry");
assert_eq!(SAMPLE[0].path, ".cargo/registry/cache");
}
#[test]
fn macro_emits_multiple_entries() {
const SAMPLE: &[DefaultRoot] = roots![
".a",
".b",
".c",
];
let labels: Vec<_> = SAMPLE.iter().map(|r| r.label).collect();
let paths: Vec<_> = SAMPLE.iter().map(|r| r.path).collect();
assert_eq!(labels, ["alpha", "beta", "gamma"]);
assert_eq!(paths, [".a", ".b", ".c"]);
}
#[test]
fn label_has_no_leading_space() {
const SAMPLE: &[DefaultRoot] = roots![
".x",
];
assert_eq!(SAMPLE[0].label, "no leading space");
assert!(!SAMPLE[0].label.starts_with(' '));
}
#[test]
fn defaults_contains_cargo_registry() {
assert!(defaults().any(|r| r.path == ".cargo/registry/cache"));
}
#[test]
fn defaults_has_no_empty_labels() {
for r in defaults() {
assert!(!r.label.is_empty(), "empty label for {}", r.path);
}
}
#[test]
fn defaults_paths_are_unique() {
let mut seen = std::collections::HashSet::new();
for r in defaults() {
assert!(seen.insert(r.path), "duplicate path {}", r.path);
}
}
}