From 261c3996805bcdfb7ff271290f3e3557dd15cea7 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 21 Feb 2026 16:59:31 +0900 Subject: feat(cache): add filesystem-backed cache with TTL expiration and size-limited GC Implement a cache module with CacheConfig and Cache structs supporting read/write (string and binary), atomic writes via temp+rename, TTL-based expiration, and size-limited garbage collection. Wire the repo cache into packagist.rs and resolver.rs for API response caching, and the files cache into downloader.rs for dist archive caching. Implement the clear-cache command with full clear and --gc modes. All existing call sites pass None for backward compatibility. Co-Authored-By: Claude Opus 4.6 --- crates/mozart/src/commands/clear_cache.rs | 44 +++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'crates/mozart/src/commands/clear_cache.rs') diff --git a/crates/mozart/src/commands/clear_cache.rs b/crates/mozart/src/commands/clear_cache.rs index 638de06..819ca9f 100644 --- a/crates/mozart/src/commands/clear_cache.rs +++ b/crates/mozart/src/commands/clear_cache.rs @@ -1,3 +1,4 @@ +use crate::cache::{Cache, build_cache_config}; use clap::Args; #[derive(Args)] @@ -7,6 +8,45 @@ pub struct ClearCacheArgs { pub gc: bool, } -pub fn execute(_args: &ClearCacheArgs, _cli: &super::Cli) -> anyhow::Result<()> { - todo!() +pub fn execute(args: &ClearCacheArgs, cli: &super::Cli) -> anyhow::Result<()> { + let config = build_cache_config(cli); + + if args.gc { + // Run GC only (probabilistic under normal circumstances, but forced here) + let repo_cache = Cache::repo(&config); + let files_cache = Cache::files(&config); + + repo_cache.gc(config.cache_ttl, u64::MAX)?; + files_cache.gc(config.cache_files_ttl, config.cache_files_maxsize)?; + + eprintln!("Cache garbage collection complete."); + eprintln!("Cache directory: {}", config.cache_dir.display()); + } else { + // Full clear of all cache directories + let repo_cache = Cache::repo(&config); + let files_cache = Cache::files(&config); + repo_cache.clear()?; + files_cache.clear()?; + // Clear anything else at the root that isn't covered by sub-caches + if config.cache_dir.exists() { + for entry in std::fs::read_dir(&config.cache_dir)? { + let entry = entry?; + let path = entry.path(); + // Skip repo/files subdirs (already cleared above) + if path == config.cache_files_dir || path == config.cache_repo_dir { + continue; + } + if path.is_file() { + std::fs::remove_file(&path)?; + } else if path.is_dir() { + std::fs::remove_dir_all(&path)?; + } + } + } + + eprintln!("Cache cleared."); + eprintln!("Cache directory: {}", config.cache_dir.display()); + } + + Ok(()) } -- cgit v1.3.1