use crate::module::traits::ModuleError;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedModule {
pub name: String,
pub version: String,
pub hash: [u8; 32],
pub manifest_hash: [u8; 32],
pub binary_hash: [u8; 32],
pub verified_at: u64,
pub verified_by: Vec<String>,
pub local_path: PathBuf,
pub expires_at: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalCache {
modules: HashMap<String, CachedModule>,
registry_state: Option<CachedRegistryState>,
last_sync: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedRegistryState {
pub root_hash: [u8; 32],
pub timestamp: u64,
pub signature: Option<String>,
}
impl LocalCache {
pub fn new() -> Self {
Self {
modules: HashMap::new(),
registry_state: None,
last_sync: 0,
}
}
pub fn load<P: AsRef<Path>>(cache_dir: P) -> Result<Self, ModuleError> {
let cache_dir = cache_dir.as_ref();
let cache_file = cache_dir.join("registry_cache.json");
if !cache_file.exists() {
return Ok(Self::new());
}
let contents = std::fs::read_to_string(&cache_file)
.map_err(|e| ModuleError::op_err("Failed to read cache file", e))?;
let cache: LocalCache = serde_json::from_str(&contents)
.map_err(|e| ModuleError::op_err("Failed to parse cache file", e))?;
Ok(cache)
}
pub fn save<P: AsRef<Path>>(&self, cache_dir: P) -> Result<(), ModuleError> {
let cache_dir = cache_dir.as_ref();
std::fs::create_dir_all(cache_dir)
.map_err(|e| ModuleError::op_err("Failed to create cache directory", e))?;
let cache_file = cache_dir.join("registry_cache.json");
let contents = serde_json::to_string_pretty(self)
.map_err(|e| ModuleError::op_err("Failed to serialize cache", e))?;
std::fs::write(&cache_file, contents)
.map_err(|e| ModuleError::op_err("Failed to write cache file", e))?;
Ok(())
}
pub fn get(&self, name: &str) -> Option<&CachedModule> {
self.modules.get(name)
}
pub fn cache(&mut self, module: CachedModule) {
self.modules.insert(module.name.clone(), module);
}
pub fn remove(&mut self, name: &str) {
self.modules.remove(name);
}
pub fn is_valid(&self, name: &str) -> bool {
if let Some(cached) = self.modules.get(name) {
if let Some(expires_at) = cached.expires_at {
let now = crate::utils::current_timestamp();
return now < expires_at;
}
return true;
}
false
}
pub fn list_modules(&self) -> Vec<String> {
self.modules.keys().cloned().collect()
}
pub fn clear_expired(&mut self) {
let now = crate::utils::current_timestamp();
self.modules.retain(|_, cached| {
if let Some(expires_at) = cached.expires_at {
now < expires_at
} else {
true
}
});
}
pub fn update_sync_time(&mut self) {
self.last_sync = crate::utils::current_timestamp();
}
pub fn last_sync(&self) -> u64 {
self.last_sync
}
}
impl Default for LocalCache {
fn default() -> Self {
Self::new()
}
}