//! `warpgate cache-list` and `warpgate cache-clean` commands. use anyhow::Result; use crate::config::Config; use crate::rclone::rc; /// List cached files via rclone RC API (aggregated across all shares). pub fn list(config: &Config) -> Result<()> { for (i, share) in config.shares.iter().enumerate() { let port = config.rc_port(i); println!("=== {} ===", share.name); let result = match rc::vfs_list(port, "/") { Ok(r) => r, Err(e) => { eprintln!(" Could not list cache for '{}': {}", share.name, e); continue; } }; let entries = if let Some(arr) = result.as_array() { arr.as_slice() } else if let Some(list) = result.get("list").and_then(|v| v.as_array()) { list.as_slice() } else { println!("{}", serde_json::to_string_pretty(&result)?); continue; }; if entries.is_empty() { println!(" Cache is empty."); continue; } println!("{:<10} PATH", "SIZE"); println!("{}", "-".repeat(60)); for entry in entries { let name = entry.get("Name").and_then(|v| v.as_str()).unwrap_or("?"); let size = entry.get("Size").and_then(|v| v.as_u64()).unwrap_or(0); let is_dir = entry .get("IsDir") .and_then(|v| v.as_bool()) .unwrap_or(false); if is_dir { println!("{:<10} {}/", "", name); } else { println!("{:<10} {}", format_bytes(size), name); } } println!(); } Ok(()) } /// Clean cached files (only clean files, never dirty). pub fn clean(config: &Config, all: bool) -> Result<()> { if all { println!("Clearing VFS directory cache for all shares..."); for (i, share) in config.shares.iter().enumerate() { let port = config.rc_port(i); match rc::vfs_forget(port, "/") { Ok(()) => println!(" {}: cleared", share.name), Err(e) => eprintln!(" {}: failed — {}", share.name, e), } } println!("Done."); } else { println!("Current cache status:"); for (i, share) in config.shares.iter().enumerate() { let port = config.rc_port(i); print!(" [{}] ", share.name); match rc::vfs_stats(port) { Ok(vfs) => { if let Some(dc) = vfs.disk_cache { println!( "Used: {}, Uploading: {}, Queued: {}", format_bytes(dc.bytes_used), dc.uploads_in_progress, dc.uploads_queued ); } else { println!("no cache stats"); } } Err(e) => println!("unreachable — {}", e), } } println!("\nRun with --all to clear the directory cache."); } Ok(()) } fn format_bytes(bytes: u64) -> String { const KIB: f64 = 1024.0; const MIB: f64 = KIB * 1024.0; const GIB: f64 = MIB * 1024.0; let b = bytes as f64; if b >= GIB { format!("{:.1} GiB", b / GIB) } else if b >= MIB { format!("{:.1} MiB", b / MIB) } else if b >= KIB { format!("{:.1} KiB", b / KIB) } else { format!("{} B", bytes) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_format_bytes_zero() { assert_eq!(format_bytes(0), "0 B"); } #[test] fn test_format_bytes_kib() { assert_eq!(format_bytes(2048), "2.0 KiB"); } #[test] fn test_format_bytes_mib() { assert_eq!(format_bytes(5242880), "5.0 MiB"); } #[test] fn test_format_bytes_gib() { assert_eq!(format_bytes(10737418240), "10.0 GiB"); } }