use std::path::PathBuf;
pub fn cache_dir() -> PathBuf {
if let Ok(home) = std::env::var("HF_HOME") {
return PathBuf::from(home).join("hub");
}
let home = std::env::var("HOME").map(PathBuf::from).unwrap_or_default();
home.join(".cache").join("huggingface").join("hub")
}
pub fn repo_has(repo: &str, files: &[&str]) -> bool {
let snapshots = cache_dir()
.join(format!("models--{}", repo.replace('/', "--")))
.join("snapshots");
let Ok(entries) = std::fs::read_dir(&snapshots) else {
return false;
};
entries
.filter_map(Result::ok)
.any(|snap| files.iter().all(|f| snap.path().join(f).exists()))
}
pub fn siglip_ready(model_id: &str) -> bool {
if !repo_has(model_id, &["config.json", "tokenizer.json"]) {
return false;
}
let snapshots = cache_dir()
.join(format!("models--{}", model_id.replace('/', "--")))
.join("snapshots");
let Ok(entries) = std::fs::read_dir(&snapshots) else {
return false;
};
entries.filter_map(Result::ok).any(|snap| {
std::fs::read_dir(snap.path())
.map(|files| {
files.filter_map(Result::ok).any(|f| {
f.path()
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("safetensors"))
})
})
.unwrap_or(false)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hf_home_overrides_the_default_location() {
let d = cache_dir();
assert!(
d.ends_with("hub"),
"cache dir should end in hub, got {}",
d.display()
);
}
#[test]
fn a_missing_repo_is_not_cached() {
assert!(!repo_has(
"definitely/not-a-real-model-xyz",
&["config.json"]
));
assert!(!siglip_ready("definitely/not-a-real-model-xyz"));
}
#[test]
fn a_repo_directory_without_the_files_is_not_cached() {
let tmp = std::env::temp_dir().join(format!("videre-hf-probe-{}", std::process::id()));
let snap = tmp
.join("hub")
.join("models--fake--repo")
.join("snapshots")
.join("abc123");
std::fs::create_dir_all(&snap).unwrap();
assert!(!snap.join("config.json").exists());
let _ = std::fs::remove_dir_all(&tmp);
}
}