1use crate::{QueryKey, QueryOptions};
4use std::any::{Any, TypeId};
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7use std::time::Instant;
8
9struct CacheEntry {
11 data: Box<dyn Any + Send + Sync>,
12 type_id: TypeId,
13 fetched_at: Instant,
14 #[allow(dead_code)]
15 last_accessed: Instant,
16 options: QueryOptions,
17 is_stale: bool,
18}
19
20pub struct QueryClient {
40 cache: Arc<RwLock<HashMap<String, CacheEntry>>>,
41 in_flight: Arc<RwLock<HashMap<String, ()>>>,
42}
43
44impl QueryClient {
45 pub fn new() -> Self {
46 Self {
47 cache: Arc::new(RwLock::new(HashMap::new())),
48 in_flight: Arc::new(RwLock::new(HashMap::new())),
49 }
50 }
51
52 pub fn get_query_data<T: Clone + Send + Sync + 'static>(&self, key: &QueryKey) -> Option<T> {
54 let cache = self.cache.read().ok()?;
55 let entry = cache.get(&key.cache_key())?;
56
57 if entry.type_id != TypeId::of::<T>() {
58 return None;
59 }
60
61 let age = entry.fetched_at.elapsed();
63 if age > entry.options.stale_time && !entry.is_stale {
64 return None;
65 }
66
67 entry.data.downcast_ref::<T>().cloned()
68 }
69
70 pub fn set_query_data<T: Clone + Send + Sync + 'static>(
72 &self,
73 key: &QueryKey,
74 data: T,
75 options: QueryOptions,
76 ) {
77 let mut cache = self.cache.write().unwrap();
78 cache.insert(
79 key.cache_key(),
80 CacheEntry {
81 data: Box::new(data),
82 type_id: TypeId::of::<T>(),
83 fetched_at: Instant::now(),
84 last_accessed: Instant::now(),
85 options,
86 is_stale: false,
87 },
88 );
89 }
90
91 pub fn invalidate_queries(&self, pattern: &QueryKey) {
93 let mut cache = self.cache.write().unwrap();
94 let pattern_str = pattern.cache_key();
95
96 for (key, entry) in cache.iter_mut() {
97 if key.starts_with(&pattern_str) || key == &pattern_str {
98 entry.is_stale = true;
99 }
100 }
101 }
102
103 pub fn is_in_flight(&self, key: &QueryKey) -> bool {
105 self.in_flight
106 .read()
107 .unwrap()
108 .contains_key(&key.cache_key())
109 }
110
111 pub fn set_in_flight(&self, key: &QueryKey, in_flight: bool) {
113 let mut map = self.in_flight.write().unwrap();
114 if in_flight {
115 map.insert(key.cache_key(), ());
116 } else {
117 map.remove(&key.cache_key());
118 }
119 }
120
121 pub fn clear(&self) {
123 self.cache.write().unwrap().clear();
124 }
125
126 #[allow(dead_code)]
128 pub fn gc(&self) {
129 let mut cache = self.cache.write().unwrap();
130 cache.retain(|_, entry| {
131 let age = entry.last_accessed.elapsed();
132 age < entry.options.gc_time
133 });
134 }
135}
136
137impl Default for QueryClient {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl Clone for QueryClient {
144 fn clone(&self) -> Self {
145 Self {
146 cache: Arc::clone(&self.cache),
147 in_flight: Arc::clone(&self.in_flight),
148 }
149 }
150}