use std::collections::HashSet;
use std::path::{Path, PathBuf};
use super::super::model::RoutineStore;
use super::{dir_size, ReapStats};
pub(super) const MAX_REPO_CACHE_DISK_BYTES_ENV: &str = "MOADIM_MAX_REPO_CACHE_DISK_BYTES";
pub(super) fn max_repo_cache_bytes() -> u64 {
std::env::var(MAX_REPO_CACHE_DISK_BYTES_ENV)
.ok()
.and_then(|value| value.parse().ok())
.unwrap_or(0)
}
pub(super) fn mirror_last_fetch_time(path: &Path) -> u64 {
std::fs::metadata(path.join("FETCH_HEAD"))
.or_else(|_err| std::fs::metadata(path))
.and_then(|meta| meta.modified())
.ok()
.and_then(|mtime| mtime.duration_since(std::time::UNIX_EPOCH).ok())
.map_or(0, |elapsed| elapsed.as_secs())
}
pub(super) fn prune_orphaned(dir: &Path, referenced: &HashSet<String>) -> ReapStats {
let Ok(entries) = std::fs::read_dir(dir) else {
return ReapStats::default();
};
let mut stats = ReapStats::default();
for entry in entries.flatten() {
if !entry.file_type().is_ok_and(|ft| ft.is_dir()) {
continue;
}
let name = entry.file_name().to_string_lossy().into_owned();
if referenced.contains(&name) {
continue;
}
let size = dir_size(&entry.path());
match std::fs::remove_dir_all(entry.path()) {
Ok(()) => {
stats.removed += 1;
stats.freed_bytes += size;
log::info!(
"cleanup: removed orphaned repo mirror {name:?} ({size} bytes) — no routine references it"
);
}
Err(err) => {
log::warn!("cleanup: failed to remove orphaned repo mirror {name:?}: {err}");
}
}
}
stats
}
pub(super) struct EvictCandidate {
pub name: String,
pub path: PathBuf,
pub size: u64,
pub last_fetch: u64,
}
pub(super) fn pick_for_eviction(
mut candidates: Vec<EvictCandidate>,
cap_bytes: u64,
total_bytes: u64,
) -> Vec<EvictCandidate> {
if cap_bytes == 0 || total_bytes <= cap_bytes {
return Vec::new();
}
candidates.sort_by_key(|candidate| candidate.last_fetch);
let mut remaining = total_bytes;
let mut chosen = Vec::new();
for candidate in candidates {
if remaining <= cap_bytes {
break;
}
remaining = remaining.saturating_sub(candidate.size);
chosen.push(candidate);
}
chosen
}
include!("enforce.rs");