use crate::ecosystem::types::*;
use std::sync::RwLock;
#[derive(Debug, Clone)]
pub struct CacheManager {
l1: Arc<Mutex<L1MemoryCache>>,
l2: Arc<RwLock<L2DiskCache>>,
l3: Arc<RwLock<L3DistributedCache>>,
}
impl CacheManager {
pub fn new() -> Self {
Self {
l1: Arc::new(Mutex::new(L1MemoryCache::new()))
l2: Arc::new(Mutex::new(L2DiskCache::new()))
l3: Arc::new(Mutex::new(L3DistributedCache::new()))
}
}
pub async fn get_package(
&self,
id: &PackageId,
) -> Result<Option<Package>, Box<dyn std::error::Error>> {
if let Some(data) = self.get_from_l1(id).await? {
return Ok(Some(deserialize_package(&data)?));
}
if let Some(data) = self.get_from_l2(id).await? {
self.store_in_l1(id, data.clone()).await?;
return Ok(Some(deserialize_package(&data)?));
}
if let Some(data) = self.get_from_l3(id).await? {
self.store_in_l2(id, data.clone()).await?;
self.store_in_l1(id, data.clone()).await?;
return Ok(Some(deserialize_package(&data)?));
}
Ok(None)
}
pub async fn store_package(
&self,
package: &Package,
) -> Result<(), Box<dyn std::error::Error>> {
let data: _ = serialize_package(package)?;
self.store_in_l1(&package.id, data.clone()).await?;
self.store_in_l2(&package.id, data.clone()).await?;
self.store_in_l3(&package.id, data).await?;
Ok(())
}
pub async fn store_in_l1(
&self,
id: &PackageId,
data: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut cache = self.l1.lock().unwrap();
cache.store(id, data);
Ok(())
}
pub async fn get_from_l1(
&self,
id: &PackageId,
) -> Result<Option<Vec<u8>, Box<dyn std::error::Error>> {
let cache: _ = self.l1.lock().unwrap();
Ok(cache.get(id))
}
pub async fn store_in_l2(
&self,
id: &PackageId,
data: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut cache = self.l2.write().await;
cache.store(id, data);
Ok(())
}
pub async fn get_from_l2(
&self,
id: &PackageId,
) -> Result<Option<Vec<u8>, Box<dyn std::error::Error>> {
let cache: _ = self.l2.read().await;
Ok(cache.get(id))
}
pub async fn store_in_l3(
&self,
id: &PackageId,
data: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut cache = self.l3.write().await;
cache.store(id, data);
Ok(())
}
pub async fn get_from_l3(
&self,
id: &PackageId,
) -> Result<Option<Vec<u8>, Box<dyn std::error::Error>> {
let cache: _ = self.l3.read().await;
Ok(cache.get(id))
}
pub async fn invalidate(&self, id: &PackageId) -> Result<(), Box<dyn std::error::Error>> {
{
let mut cache = self.l1.lock().unwrap();
cache.invalidate(id);
}
{
let mut cache = self.l2.write().await;
cache.invalidate(id);
}
{
let mut cache = self.l3.write().await;
cache.invalidate(id);
}
Ok(())
}
pub async fn is_cached(&self, id: &PackageId) -> Result<bool, Box<dyn std::error::Error>> {
if self.get_from_l1(id).await?.is_some() {
return Ok(true);
}
if self.get_from_l2(id).await?.is_some() {
return Ok(true);
}
if self.get_from_l3(id).await?.is_some() {
return Ok(true);
}
Ok(false)
}
pub async fn store_package_id(
&self,
id: &PackageId,
) -> Result<(), Box<dyn std::error::Error>> {
let data: _ = serialize_package_id(id)?;
self.store_in_l1(id, data).await?;
Ok(())
}
pub async fn prefetch_popular_packages(
&self,
packages: &[PackageId],
) -> Result<PrefetchResult, Box<dyn std::error::Error>> {
let mut prefetched_count = 0;
for package_id in packages {
if self.get_package(package_id).await?.is_none() {
let mock_package: _ = create_mock_package(package_id);
self.store_package(&mock_package).await?;
prefetched_count += 1;
}
}
Ok(PrefetchResult {
prefetched_count: prefetched_count as u64,
cache_hit_rate: 0.0, })
}
pub async fn get_cached_packages(
&self,
dependencies: &DependencyGraph,
) -> Result<Vec<Package>, Box<dyn std::error::Error>> {
let mut cached = Vec::new();
for (name, version) in &dependencies.nodes {
let package_id: _ = PackageId {
name: name.clone(),
version: version.clone(),
};
if let Some(package) = self.get_package(&package_id).await? {
cached.push(package);
}
}
Ok(cached)
}
pub async fn update(&self, installed: &[Package]) -> Result<(), Box<dyn std::error::Error>> {
for package in installed {
self.store_package(package).await?;
}
Ok(())
}
pub async fn identify_missing_packages(
&self,
dependencies: &DependencyGraph,
cached: &[Package],
) -> Vec<PackageInfo> {
let cached_names: std::collections::HashSet<String> =
cached.iter().map(|p| p.id.name.clone()).collect();
let mut missing = Vec::new();
for (name, version) in &dependencies.nodes {
if !cached_names.contains(name) {
missing.push(PackageInfo {
name: name.clone(),
version: version.clone(),
download_url: format!("https://example.com/{}-{}.tgz", name, version),
checksum: format!("checksum-{}-{}", name, version),
available_versions: vec![version.clone()],
manifest: PackageManifest {
name: name.clone(),
version: version.clone(),
dependencies: HashMap::new(),
dev_dependencies: HashMap::new(),
},
});
}
}
missing
}
}
#[derive(Debug, Clone)]
struct L1MemoryCache {
cache: HashMap<String, Vec<u8>>,
}
impl L1MemoryCache {
fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
fn store(&mut self, id: &PackageId, data: Vec<u8>) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.insert(key, data);
}
fn get(&self, id: &PackageId) -> Option<Vec<u8> {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.get(&key).cloned()
}
fn invalidate(&mut self, id: &PackageId) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.remove(&key);
}
}
#[derive(Debug, Clone)]
struct L2DiskCache {
cache: HashMap<String, Vec<u8>>,
}
impl L2DiskCache {
fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
fn store(&mut self, id: &PackageId, data: Vec<u8>) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.insert(key, data);
}
fn get(&self, id: &PackageId) -> Option<Vec<u8> {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.get(&key).cloned()
}
fn invalidate(&mut self, id: &PackageId) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.remove(&key);
}
}
#[derive(Debug, Clone)]
struct L3DistributedCache {
cache: HashMap<String, Vec<u8>>,
}
impl L3DistributedCache {
fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
fn store(&mut self, id: &PackageId, data: Vec<u8>) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.insert(key, data);
}
fn get(&self, id: &PackageId) -> Option<Vec<u8> {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.get(&key).cloned()
}
fn invalidate(&mut self, id: &PackageId) {
let key: _ = format!("{}@{}, id.name", id.version));
self.cache.remove(&key);
}
}
fn serialize_package(package: &Package) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(bincode::serialize(package)?)
}
fn deserialize_package(data: &[u8]) -> Result<Package, Box<dyn std::error::Error>> {
Ok(bincode::deserialize(data)?)
}
fn serialize_package_id(id: &PackageId) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
Ok(bincode::serialize(id)?)
}
fn create_mock_package(id: &PackageId) -> Package {
Package {
id: id.clone(),
manifest: PackageManifest {
name: id.name.clone(),
version: id.version.clone(),
dependencies: HashMap::new(),
dev_dependencies: HashMap::new(),
},
tarball: vec![],
}
}
use std::collections::HashSet;
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};