use super::manager::SshConfigCache;
use anyhow::Result;
use std::collections::HashMap;
use std::path::PathBuf;
use tracing::debug;
impl SshConfigCache {
pub async fn maintain(&self) -> Result<usize> {
if !self.config.enabled {
return Ok(0);
}
let mut to_remove = Vec::new();
let mut expired_count = 0;
let mut stale_count = 0;
let mut check_tasks = Vec::new();
{
let cache = self
.cache
.write()
.map_err(|e| anyhow::anyhow!("Cache write lock poisoned in maintain: {e}"))?;
for (path, entry) in cache.iter() {
if entry.is_expired(self.config.ttl) {
to_remove.push(path.clone());
expired_count += 1;
} else {
let path_clone = path.clone();
let entry_mtime = entry.file_mtime;
check_tasks.push(tokio::spawn(async move {
if let Ok(metadata) = tokio::fs::metadata(&path_clone).await {
if let Ok(current_mtime) = metadata.modified() {
(path_clone, entry_mtime != current_mtime, true)
} else {
(path_clone, false, false)
}
} else {
(path_clone, true, false)
}
}));
}
}
}
for task in check_tasks {
if let Ok((path, is_stale, _file_exists)) = task.await {
if is_stale {
to_remove.push(path);
stale_count += 1;
}
}
}
{
let mut cache = self.cache.write().map_err(|e| {
anyhow::anyhow!("Cache write lock poisoned during maintenance cleanup: {e}")
})?;
for path in &to_remove {
cache.pop(path);
}
}
let removed_count = to_remove.len();
{
let cache = self.cache.read().map_err(|e| {
anyhow::anyhow!("Cache read lock poisoned during maintenance stats: {e}")
})?;
let mut stats = self.stats.write().map_err(|e| {
anyhow::anyhow!("Stats write lock poisoned during maintenance: {e}")
})?;
stats.ttl_evictions += expired_count as u64;
stats.stale_evictions += stale_count as u64;
stats.current_entries = cache.len();
}
if removed_count > 0 {
debug!(
"SSH config cache maintenance: removed {} entries ({} expired, {} stale)",
removed_count, expired_count, stale_count
);
}
Ok(removed_count)
}
pub fn debug_info(&self) -> Result<HashMap<PathBuf, String>> {
let cache = self
.cache
.read()
.map_err(|e| anyhow::anyhow!("Cache read lock poisoned in debug_info: {e}"))?;
let mut info = HashMap::new();
for (path, entry) in cache.iter() {
let age = entry.age();
let is_expired = entry.is_expired(self.config.ttl);
let last_accessed = entry.time_since_last_access();
let status = if is_expired { "EXPIRED" } else { "VALID" };
info.insert(
path.clone(),
format!(
"Status: {}, Age: {:?}, Accesses: {}, Last accessed: {:?} ago",
status, age, entry.access_count, last_accessed
),
);
}
Ok(info)
}
}