initial commit

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
This commit is contained in:
raf 2026-01-30 22:05:46 +03:00
commit 6a73d11c4b
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
124 changed files with 34856 additions and 0 deletions

View file

@ -0,0 +1,91 @@
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
struct CacheEntry<V> {
value: V,
inserted_at: Instant,
}
/// A simple TTL-based in-memory cache with periodic eviction.
pub struct Cache<K, V> {
entries: Arc<RwLock<HashMap<K, CacheEntry<V>>>>,
ttl: Duration,
}
impl<K, V> Cache<K, V>
where
K: Eq + Hash + Clone + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
{
pub fn new(ttl: Duration) -> Self {
let cache = Self {
entries: Arc::new(RwLock::new(HashMap::new())),
ttl,
};
// Spawn periodic eviction task
let entries = cache.entries.clone();
let ttl = cache.ttl;
tokio::spawn(async move {
let mut interval = tokio::time::interval(ttl);
loop {
interval.tick().await;
let now = Instant::now();
let mut map = entries.write().await;
map.retain(|_, entry| now.duration_since(entry.inserted_at) < ttl);
}
});
cache
}
pub async fn get(&self, key: &K) -> Option<V> {
let map = self.entries.read().await;
if let Some(entry) = map.get(key) {
if entry.inserted_at.elapsed() < self.ttl {
return Some(entry.value.clone());
}
}
None
}
pub async fn insert(&self, key: K, value: V) {
let mut map = self.entries.write().await;
map.insert(
key,
CacheEntry {
value,
inserted_at: Instant::now(),
},
);
}
pub async fn invalidate(&self, key: &K) {
let mut map = self.entries.write().await;
map.remove(key);
}
pub async fn invalidate_all(&self) {
let mut map = self.entries.write().await;
map.clear();
}
}
/// Application-level cache layer wrapping multiple caches for different data types.
pub struct CacheLayer {
/// Cache for serialized API responses, keyed by request path + query string.
pub responses: Cache<String, String>,
}
impl CacheLayer {
pub fn new(ttl_secs: u64) -> Self {
let ttl = Duration::from_secs(ttl_secs);
Self {
responses: Cache::new(ttl),
}
}
}