1use std::sync::atomic::{AtomicU64, Ordering};
4use std::time::{Duration, Instant};
5use serde::{Serialize, Deserialize};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum QueryStatus {
11 Idle,
13 Loading,
15 Success,
17 Error,
19}
20
21impl Default for QueryStatus {
22 fn default() -> Self {
23 Self::Idle
24 }
25}
26
27#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
29pub struct QueryKey {
30 pub segments: Vec<String>,
31}
32
33impl fmt::Display for QueryKey {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(f, "{}", self.segments.join(":"))
36 }
37}
38
39impl QueryKey {
40 pub fn new(segments: impl IntoIterator<Item = impl ToString>) -> Self {
42 Self {
43 segments: segments.into_iter().map(|s| s.to_string()).collect(),
44 }
45 }
46
47 pub fn from_string(s: impl Into<String>) -> Self {
49 Self {
50 segments: vec![s.into()],
51 }
52 }
53
54 pub fn with_segment(mut self, segment: impl Into<String>) -> Self {
56 self.segments.push(segment.into());
57 self
58 }
59
60 pub fn segments(&self) -> &[String] {
62 &self.segments
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.segments.is_empty()
68 }
69
70 pub fn len(&self) -> usize {
72 self.segments.len()
73 }
74
75 pub fn matches_pattern(&self, pattern: &QueryKeyPattern) -> bool {
77 match pattern {
78 QueryKeyPattern::Exact(key) => self == key,
79 QueryKeyPattern::Prefix(prefix) => {
80 self.segments.len() >= prefix.segments.len() &&
81 self.segments[..prefix.segments.len()] == prefix.segments
82 }
83 QueryKeyPattern::Contains(substring) => {
84 self.segments.iter().any(|segment| segment.contains(substring))
85 }
86 }
87 }
88}
89
90impl<T: ToString + std::fmt::Display> From<&[T]> for QueryKey {
92 fn from(segments: &[T]) -> Self {
93 Self::new(segments)
94 }
95}
96
97impl From<String> for QueryKey {
99 fn from(segment: String) -> Self {
100 Self::new([segment])
101 }
102}
103
104impl From<&str> for QueryKey {
106 fn from(segment: &str) -> Self {
107 Self::new([segment.to_string()])
108 }
109}
110
111impl<T: ToString + std::fmt::Display> From<(T,)> for QueryKey {
113 fn from((a,): (T,)) -> Self {
114 Self::new([a])
115 }
116}
117
118impl<const N: usize> From<[&str; N]> for QueryKey {
120 fn from(segments: [&str; N]) -> Self {
121 Self::new(&segments)
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub enum QueryKeyPattern {
128 Exact(QueryKey),
130 Prefix(QueryKey),
132 Contains(String),
134}
135
136#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
138pub struct QueryObserverId {
139 pub id: u64,
140}
141
142impl QueryObserverId {
143 pub fn new() -> Self {
145 static COUNTER: AtomicU64 = AtomicU64::new(0);
146 Self {
147 id: COUNTER.fetch_add(1, Ordering::Relaxed),
148 }
149 }
150}
151
152impl Default for QueryObserverId {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct QueryMeta {
161 pub status: QueryStatus,
162 #[serde(with = "instant_serde")]
163 pub updated_at: Instant,
164 #[serde(with = "duration_serde")]
165 pub stale_time: Duration,
166 #[serde(with = "duration_serde")]
167 pub cache_time: Duration,
168}
169
170impl QueryMeta {
171 pub fn is_stale(&self) -> bool {
173 let age = Instant::now().duration_since(self.updated_at);
174 age > self.stale_time
175 }
176
177 pub fn is_expired(&self) -> bool {
179 let age = Instant::now().duration_since(self.updated_at);
180 age > self.cache_time
181 }
182}
183
184impl Default for QueryMeta {
185 fn default() -> Self {
186 Self {
187 status: QueryStatus::Idle,
188 updated_at: Instant::now(),
189 stale_time: Duration::from_secs(0),
190 cache_time: Duration::from_secs(5 * 60), }
192 }
193}
194
195mod instant_serde {
197 use serde::{Deserialize, Deserializer, Serialize, Serializer};
198 use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
199
200 pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
201 where
202 S: Serializer,
203 {
204 let system_time = SystemTime::now() - instant.elapsed();
206 let duration = system_time.duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO);
207 duration.serialize(serializer)
208 }
209
210 pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
211 where
212 D: Deserializer<'de>,
213 {
214 let duration = Duration::deserialize(deserializer)?;
215 let system_time = UNIX_EPOCH + duration;
216 let now = SystemTime::now();
217 let elapsed = now.duration_since(system_time).unwrap_or(Duration::ZERO);
218 Ok(Instant::now() - elapsed)
219 }
220}
221
222mod duration_serde {
224 use serde::{Deserialize, Deserializer, Serialize, Serializer};
225 use std::time::Duration;
226
227 pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
228 where
229 S: Serializer,
230 {
231 duration.as_secs().serialize(serializer)
232 }
233
234 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
235 where
236 D: Deserializer<'de>,
237 {
238 let secs = u64::deserialize(deserializer)?;
239 Ok(Duration::from_secs(secs))
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_query_key_creation() {
249 let key = QueryKey::new(["users", "123"]);
250 assert_eq!(key.segments, vec!["users", "123"]);
251
252 let key2 = QueryKey::from("single");
253 assert_eq!(key2.segments, vec!["single"]);
254 }
255
256 #[test]
257 fn test_query_key_pattern_matching() {
258 let key = QueryKey::new(["users", "123", "profile"]);
259
260 let exact_pattern = QueryKeyPattern::Exact(QueryKey::new(["users", "123", "profile"]));
262 assert!(key.matches_pattern(&exact_pattern));
263
264 let prefix_pattern = QueryKeyPattern::Prefix(QueryKey::new(["users"]));
266 assert!(key.matches_pattern(&prefix_pattern));
267
268 let contains_pattern = QueryKeyPattern::Contains("123".to_string());
270 assert!(key.matches_pattern(&contains_pattern));
271 }
272
273 #[test]
274 fn test_query_meta_stale_check() {
275 let mut meta = QueryMeta::default();
276 meta.stale_time = Duration::from_secs(60);
277
278 assert!(!meta.is_stale());
280
281 meta.updated_at = Instant::now() - Duration::from_secs(120);
283 assert!(meta.is_stale());
284 }
285}