use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
#[derive(Debug, Clone)]
struct CacheEntry {
value: Value,
expires_at: Instant,
}
#[derive(Debug, Clone)]
pub struct CacheConfig {
pub max_entries: usize,
pub default_ttl: Duration,
pub enable_metrics: bool,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
max_entries: 1000,
default_ttl: Duration::from_secs(300), enable_metrics: true,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct CacheMetrics {
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub total_requests: u64,
}
impl CacheMetrics {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn hit_ratio(&self) -> f64 {
if self.total_requests == 0 {
0.0
} else {
self.hits as f64 / self.total_requests as f64
}
}
}
pub struct McpCache {
entries: Arc<RwLock<HashMap<String, CacheEntry>>>,
config: CacheConfig,
metrics: Arc<RwLock<CacheMetrics>>,
}
impl Default for McpCache {
fn default() -> Self {
Self::new()
}
}
impl McpCache {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self::with_config(CacheConfig::default())
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn with_config(config: CacheConfig) -> Self {
Self {
entries: Arc::new(RwLock::new(HashMap::new())),
config,
metrics: Arc::new(RwLock::new(CacheMetrics::default())),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn get(&self, key: &str) -> Option<Value> {
let mut metrics = self.metrics.write().await;
metrics.total_requests += 1;
let entries = self.entries.read().await;
if let Some(entry) = entries.get(key) {
if entry.expires_at > Instant::now() {
metrics.hits += 1;
return Some(entry.value.clone());
}
}
metrics.misses += 1;
None
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn set(&self, key: String, value: Value) {
self.set_with_ttl(key, value, self.config.default_ttl).await;
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn set_with_ttl(&self, key: String, value: Value, ttl: Duration) {
let mut entries = self.entries.write().await;
if entries.len() >= self.config.max_entries {
self.evict_expired(&mut entries).await;
if entries.len() >= self.config.max_entries {
if let Some(oldest_key) = self.find_oldest(&entries) {
entries.remove(&oldest_key);
if self.config.enable_metrics {
let mut metrics = self.metrics.write().await;
metrics.evictions += 1;
}
}
}
}
entries.insert(
key,
CacheEntry {
value,
expires_at: Instant::now() + ttl,
},
);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn clear(&self) {
let mut entries = self.entries.write().await;
entries.clear();
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn metrics(&self) -> CacheMetrics {
self.metrics.read().await.clone()
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn size(&self) -> usize {
self.entries.read().await.len()
}
async fn evict_expired(&self, entries: &mut HashMap<String, CacheEntry>) {
let now = Instant::now();
let expired_keys: Vec<String> = entries
.iter()
.filter(|(_, entry)| entry.expires_at <= now)
.map(|(key, _)| key.clone())
.collect();
for key in expired_keys {
entries.remove(&key);
if self.config.enable_metrics {
let mut metrics = self.metrics.write().await;
metrics.evictions += 1;
}
}
}
fn find_oldest(&self, entries: &HashMap<String, CacheEntry>) -> Option<String> {
entries
.iter()
.min_by_key(|(_, entry)| entry.expires_at)
.map(|(key, _)| key.clone())
}
}
pub struct CacheKeyBuilder;
impl CacheKeyBuilder {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn analysis_key(file_path: &str, version: &str) -> String {
format!("analysis:{file_path}:{version}")
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn refactor_plan_key(file_path: &str, config_hash: u64) -> String {
format!("refactor_plan:{file_path}:{config_hash}")
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn complexity_key(file_path: &str) -> String {
format!("complexity:{file_path}")
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn method_result_key(method: &str, params_hash: u64) -> String {
format!("method:{method}:{params_hash}")
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_cache_basic_operations() {
let cache = McpCache::new();
cache
.set(
"test_key".to_string(),
Value::String("test_value".to_string()),
)
.await;
let value = cache.get("test_key").await;
assert_eq!(value, Some(Value::String("test_value".to_string())));
let missing = cache.get("missing_key").await;
assert_eq!(missing, None);
}
#[tokio::test]
async fn test_cache_expiration() {
let cache = McpCache::new();
cache
.set_with_ttl(
"expire_key".to_string(),
Value::String("expire_value".to_string()),
Duration::from_millis(10),
)
.await;
assert!(cache.get("expire_key").await.is_some());
tokio::time::sleep(Duration::from_millis(20)).await;
assert!(cache.get("expire_key").await.is_none());
}
#[tokio::test]
async fn test_cache_metrics() {
let cache = McpCache::new();
cache
.set("key1".to_string(), Value::String("value1".to_string()))
.await;
let _ = cache.get("key1").await; let _ = cache.get("key2").await; let _ = cache.get("key1").await;
let metrics = cache.metrics().await;
assert_eq!(metrics.hits, 2);
assert_eq!(metrics.misses, 1);
assert_eq!(metrics.total_requests, 3);
assert_eq!(metrics.hit_ratio(), 2.0 / 3.0);
}
#[tokio::test]
async fn test_cache_eviction() {
let config = CacheConfig {
max_entries: 2,
default_ttl: Duration::from_secs(60),
enable_metrics: true,
};
let cache = McpCache::with_config(config);
cache
.set("key1".to_string(), Value::String("value1".to_string()))
.await;
cache
.set("key2".to_string(), Value::String("value2".to_string()))
.await;
cache
.set("key3".to_string(), Value::String("value3".to_string()))
.await;
assert!(cache.size().await <= 2);
}
#[test]
fn test_cache_key_builder() {
let analysis_key = CacheKeyBuilder::analysis_key("src/main.rs", "v1.0.0");
assert_eq!(analysis_key, "analysis:src/main.rs:v1.0.0");
let refactor_key = CacheKeyBuilder::refactor_plan_key("src/lib.rs", 12345);
assert_eq!(refactor_key, "refactor_plan:src/lib.rs:12345");
let complexity_key = CacheKeyBuilder::complexity_key("src/test.rs");
assert_eq!(complexity_key, "complexity:src/test.rs");
let method_key = CacheKeyBuilder::method_result_key("refactor.start", 67890);
assert_eq!(method_key, "method:refactor.start:67890");
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}