use serde::{Deserialize, Serialize};
use std::collections::HashMap;
const CACHE_VERSION: u32 = 1;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemoteCacheEntry {
pub last_modified: String,
pub response_json: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RemoteNodeCache {
version: u32,
entries: HashMap<String, RemoteCacheEntry>,
doc_timestamps: HashMap<String, String>,
}
impl RemoteNodeCache {
pub fn new() -> Self {
RemoteNodeCache {
version: CACHE_VERSION,
entries: HashMap::new(),
doc_timestamps: HashMap::new(),
}
}
fn make_key(doc_id: &str, node_id: &str) -> String {
format!("{}:{}", doc_id, node_id)
}
pub fn get(&self, doc_id: &str, node_id: &str, current_last_modified: &str) -> Option<&str> {
let key = Self::make_key(doc_id, node_id);
if let Some(entry) = self.entries.get(&key) {
if entry.last_modified == current_last_modified {
return Some(&entry.response_json);
}
}
None
}
pub fn put(&mut self, doc_id: &str, node_id: &str, last_modified: &str, response_json: String) {
let key = Self::make_key(doc_id, node_id);
self.entries.insert(
key,
RemoteCacheEntry { last_modified: last_modified.to_string(), response_json },
);
self.doc_timestamps.insert(doc_id.to_string(), last_modified.to_string());
}
pub fn doc_last_modified(&self, doc_id: &str) -> Option<&str> {
self.doc_timestamps.get(doc_id).map(|s| s.as_str())
}
pub fn set_doc_last_modified(&mut self, doc_id: &str, last_modified: &str) {
self.doc_timestamps.insert(doc_id.to_string(), last_modified.to_string());
}
pub fn is_valid_version(&self) -> bool {
self.version == CACHE_VERSION
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for RemoteNodeCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_hit() {
let mut cache = RemoteNodeCache::new();
cache.put("doc1", "node1", "2024-01-01", "{\"test\": true}".to_string());
assert_eq!(cache.get("doc1", "node1", "2024-01-01"), Some("{\"test\": true}"));
}
#[test]
fn test_cache_miss_on_stale_timestamp() {
let mut cache = RemoteNodeCache::new();
cache.put("doc1", "node1", "2024-01-01", "{\"test\": true}".to_string());
assert_eq!(cache.get("doc1", "node1", "2024-01-02"), None);
}
#[test]
fn test_cache_miss_on_unknown_key() {
let cache = RemoteNodeCache::new();
assert_eq!(cache.get("doc1", "node1", "2024-01-01"), None);
}
#[test]
fn test_doc_timestamps() {
let mut cache = RemoteNodeCache::new();
cache.set_doc_last_modified("doc1", "2024-01-01");
assert_eq!(cache.doc_last_modified("doc1"), Some("2024-01-01"));
assert_eq!(cache.doc_last_modified("doc2"), None);
}
#[test]
fn test_version_check() {
let cache = RemoteNodeCache::new();
assert!(cache.is_valid_version());
}
#[test]
fn test_serialization_roundtrip() {
let mut cache = RemoteNodeCache::new();
cache.put("doc1", "node1", "2024-01-01", "{\"nodes\": {}}".to_string());
let json = serde_json::to_string(&cache).unwrap();
let restored: RemoteNodeCache = serde_json::from_str(&json).unwrap();
assert!(restored.is_valid_version());
assert_eq!(restored.get("doc1", "node1", "2024-01-01"), Some("{\"nodes\": {}}"));
}
}