use std::collections::{HashMap, HashSet};
#[derive(Debug)]
pub struct DependencyTracker {
cache_to_views: HashMap<String, Vec<String>>,
view_to_caches: HashMap<String, HashSet<String>>,
}
impl DependencyTracker {
#[must_use]
pub fn new() -> Self {
Self {
cache_to_views: HashMap::new(),
view_to_caches: HashMap::new(),
}
}
#[allow(clippy::needless_pass_by_value)] pub fn record_access(&mut self, cache_key: String, views: Vec<String>) {
if let Some(old_views) = self.cache_to_views.get(&cache_key) {
for old_view in old_views {
if let Some(caches) = self.view_to_caches.get_mut(old_view) {
caches.remove(&cache_key);
if caches.is_empty() {
self.view_to_caches.remove(old_view);
}
}
}
}
self.cache_to_views.insert(cache_key.clone(), views.clone());
for view in views {
self.view_to_caches.entry(view).or_default().insert(cache_key.clone());
}
}
#[must_use]
pub fn get_dependent_caches(&self, view: &str) -> Vec<String> {
self.view_to_caches
.get(view)
.map(|set| set.iter().cloned().collect())
.unwrap_or_default()
}
pub fn remove_cache(&mut self, cache_key: &str) {
if let Some(views) = self.cache_to_views.remove(cache_key) {
for view in views {
if let Some(caches) = self.view_to_caches.get_mut(&view) {
caches.remove(cache_key);
if caches.is_empty() {
self.view_to_caches.remove(&view);
}
}
}
}
}
pub fn clear(&mut self) {
self.cache_to_views.clear();
self.view_to_caches.clear();
}
#[must_use]
pub fn cache_count(&self) -> usize {
self.cache_to_views.len()
}
#[must_use]
pub fn view_count(&self) -> usize {
self.view_to_caches.len()
}
#[must_use]
pub fn get_all_views(&self) -> Vec<String> {
self.view_to_caches.keys().cloned().collect()
}
}
impl Default for DependencyTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_record_and_get_dependency() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
let affected = tracker.get_dependent_caches("v_user");
assert_eq!(affected.len(), 1);
assert!(affected.contains(&"key1".to_string()));
}
#[test]
fn test_multiple_caches_same_view() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.record_access("key2".to_string(), vec!["v_user".to_string()]);
let affected = tracker.get_dependent_caches("v_user");
assert_eq!(affected.len(), 2);
assert!(affected.contains(&"key1".to_string()));
assert!(affected.contains(&"key2".to_string()));
}
#[test]
fn test_cache_accesses_multiple_views() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string(), "v_post".to_string()]);
let user_caches = tracker.get_dependent_caches("v_user");
let post_caches = tracker.get_dependent_caches("v_post");
assert!(user_caches.contains(&"key1".to_string()));
assert!(post_caches.contains(&"key1".to_string()));
}
#[test]
fn test_remove_cache() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.remove_cache("key1");
let affected = tracker.get_dependent_caches("v_user");
assert_eq!(affected.len(), 0);
}
#[test]
fn test_remove_cache_with_multiple_views() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string(), "v_post".to_string()]);
tracker.remove_cache("key1");
assert_eq!(tracker.get_dependent_caches("v_user").len(), 0);
assert_eq!(tracker.get_dependent_caches("v_post").len(), 0);
}
#[test]
fn test_remove_nonexistent_cache() {
let mut tracker = DependencyTracker::new();
tracker.remove_cache("nonexistent");
}
#[test]
fn test_get_nonexistent_view() {
let tracker = DependencyTracker::new();
let affected = tracker.get_dependent_caches("nonexistent");
assert_eq!(affected.len(), 0);
}
#[test]
fn test_clear() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.record_access("key2".to_string(), vec!["v_post".to_string()]);
tracker.clear();
assert_eq!(tracker.cache_count(), 0);
assert_eq!(tracker.view_count(), 0);
assert_eq!(tracker.get_dependent_caches("v_user").len(), 0);
}
#[test]
fn test_cache_count() {
let mut tracker = DependencyTracker::new();
assert_eq!(tracker.cache_count(), 0);
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
assert_eq!(tracker.cache_count(), 1);
tracker.record_access("key2".to_string(), vec!["v_post".to_string()]);
assert_eq!(tracker.cache_count(), 2);
tracker.remove_cache("key1");
assert_eq!(tracker.cache_count(), 1);
}
#[test]
fn test_view_count() {
let mut tracker = DependencyTracker::new();
assert_eq!(tracker.view_count(), 0);
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
assert_eq!(tracker.view_count(), 1);
tracker.record_access("key2".to_string(), vec!["v_user".to_string(), "v_post".to_string()]);
assert_eq!(tracker.view_count(), 2); }
#[test]
fn test_get_all_views() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.record_access("key2".to_string(), vec!["v_post".to_string()]);
let views = tracker.get_all_views();
assert_eq!(views.len(), 2);
assert!(views.contains(&"v_user".to_string()));
assert!(views.contains(&"v_post".to_string()));
}
#[test]
fn test_update_access_overwrites() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.record_access("key1".to_string(), vec!["v_post".to_string()]);
assert_eq!(tracker.get_dependent_caches("v_user").len(), 0);
assert_eq!(tracker.get_dependent_caches("v_post").len(), 1);
}
#[test]
fn test_bidirectional_consistency() {
let mut tracker = DependencyTracker::new();
tracker.record_access("key1".to_string(), vec!["v_user".to_string()]);
tracker.record_access("key2".to_string(), vec!["v_user".to_string(), "v_post".to_string()]);
assert_eq!(tracker.cache_count(), 2);
assert_eq!(tracker.get_dependent_caches("v_user").len(), 2);
assert_eq!(tracker.get_dependent_caches("v_post").len(), 1);
tracker.remove_cache("key1");
assert_eq!(tracker.cache_count(), 1);
assert_eq!(tracker.get_dependent_caches("v_user").len(), 1);
assert_eq!(tracker.get_dependent_caches("v_post").len(), 1);
}
}