Skip to main content
Glama

tfmcp

by nwiizo
cache.rs5.07 kB
use std::collections::HashMap; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::sync::RwLock; #[derive(Clone)] struct CachedItem<T> { value: T, inserted_at: Instant, } /// Simple in-memory cache with TTL support pub struct SimpleCache<T: Clone> { storage: Arc<RwLock<HashMap<String, CachedItem<T>>>>, ttl: Duration, } impl<T: Clone> SimpleCache<T> { pub fn new(ttl: Duration) -> Self { Self { storage: Arc::new(RwLock::new(HashMap::new())), ttl, } } /// Get value from cache if it exists and hasn't expired pub async fn get(&self, key: &str) -> Option<T> { let storage = self.storage.read().await; if let Some(item) = storage.get(key) { if item.inserted_at.elapsed() < self.ttl { return Some(item.value.clone()); } } None } /// Set value in cache pub async fn set(&self, key: String, value: T) { let mut storage = self.storage.write().await; storage.insert( key, CachedItem { value, inserted_at: Instant::now(), }, ); } /// Remove expired entries from cache #[allow(dead_code)] pub async fn cleanup_expired(&self) { let mut storage = self.storage.write().await; storage.retain(|_, item| item.inserted_at.elapsed() < self.ttl); } /// Get cache statistics #[allow(dead_code)] pub async fn stats(&self) -> CacheStats { let storage = self.storage.read().await; let total_entries = storage.len(); let valid_entries = storage .values() .filter(|item| item.inserted_at.elapsed() < self.ttl) .count(); let expired_entries = total_entries - valid_entries; CacheStats { total_entries, valid_entries, expired_entries, } } /// Clear all cache entries #[allow(dead_code)] pub async fn clear(&self) { let mut storage = self.storage.write().await; storage.clear(); } /// Get cache hit rate (requires tracking hits and misses) #[allow(dead_code)] pub async fn size(&self) -> usize { let storage = self.storage.read().await; storage.len() } } #[derive(Debug)] pub struct CacheStats { pub total_entries: usize, pub valid_entries: usize, pub expired_entries: usize, } impl<T: Clone> Default for SimpleCache<T> { fn default() -> Self { Self::new(Duration::from_secs(300)) // 5 minutes default TTL } } /// Specialized cache for documentation content pub type DocumentationCache = SimpleCache<String>; /// Specialized cache for provider information pub type ProvidersCache = SimpleCache<String>; /// Cache manager that handles multiple cache types pub struct CacheManager { pub documentation_cache: DocumentationCache, pub providers_cache: ProvidersCache, } impl CacheManager { pub fn new() -> Self { Self { documentation_cache: SimpleCache::new(Duration::from_secs(1800)), // 30 minutes for docs providers_cache: SimpleCache::new(Duration::from_secs(600)), // 10 minutes for providers } } /// Get comprehensive cache statistics #[allow(dead_code)] pub async fn global_stats(&self) -> HashMap<String, CacheStats> { let mut stats = HashMap::new(); stats.insert( "documentation".to_string(), self.documentation_cache.stats().await, ); stats.insert("providers".to_string(), self.providers_cache.stats().await); stats } /// Cleanup expired entries from all caches #[allow(dead_code)] pub async fn cleanup_all(&self) { self.documentation_cache.cleanup_expired().await; self.providers_cache.cleanup_expired().await; } /// Clear all caches #[allow(dead_code)] pub async fn clear_all(&self) { self.documentation_cache.clear().await; self.providers_cache.clear().await; } } impl Default for CacheManager { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use tokio::time::{sleep, Duration}; #[tokio::test] async fn test_cache_basic_operations() { let cache = SimpleCache::new(Duration::from_millis(100)); // Test set and get cache.set("key1".to_string(), "value1".to_string()).await; assert_eq!(cache.get("key1").await, Some("value1".to_string())); // Test non-existent key assert_eq!(cache.get("nonexistent").await, None); } #[tokio::test] async fn test_cache_expiration() { let cache = SimpleCache::new(Duration::from_millis(50)); cache.set("key1".to_string(), "value1".to_string()).await; assert_eq!(cache.get("key1").await, Some("value1".to_string())); // Wait for expiration sleep(Duration::from_millis(60)).await; assert_eq!(cache.get("key1").await, None); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nwiizo/tfmcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server