1use std::hash::{Hash, Hasher};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct QueryKey {
21 segments: Vec<KeySegment>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25enum KeySegment {
26 Static(String),
27 Dynamic(String, String), }
29
30impl QueryKey {
31 pub fn new(root: impl Into<String>) -> Self {
33 Self {
34 segments: vec![KeySegment::Static(root.into())],
35 }
36 }
37
38 pub fn segment(mut self, segment: impl Into<String>) -> Self {
40 self.segments.push(KeySegment::Static(segment.into()));
41 self
42 }
43
44 pub fn with(mut self, name: impl Into<String>, value: impl ToString) -> Self {
46 self.segments
47 .push(KeySegment::Dynamic(name.into(), value.to_string()));
48 self
49 }
50
51 pub fn matches(&self, pattern: &QueryKey) -> bool {
54 if pattern.segments.len() > self.segments.len() {
55 return false;
56 }
57 self.segments
58 .iter()
59 .zip(pattern.segments.iter())
60 .all(|(a, b)| a == b)
61 }
62
63 pub fn cache_key(&self) -> String {
65 self.segments
66 .iter()
67 .map(|s| match s {
68 KeySegment::Static(v) => v.clone(),
69 KeySegment::Dynamic(k, v) => format!("{}={}", k, v),
70 })
71 .collect::<Vec<_>>()
72 .join("::")
73 }
74}
75
76impl Hash for QueryKey {
77 fn hash<H: Hasher>(&self, state: &mut H) {
78 self.cache_key().hash(state);
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_simple_key() {
88 let key = QueryKey::new("users");
89 assert_eq!(key.cache_key(), "users");
90 }
91
92 #[test]
93 fn test_key_with_params() {
94 let key = QueryKey::new("users").with("id", 123);
95 assert_eq!(key.cache_key(), "users::id=123");
96 }
97
98 #[test]
99 fn test_hierarchical_key() {
100 let key = QueryKey::new("users").segment("posts").with("id", 456);
101 assert_eq!(key.cache_key(), "users::posts::id=456");
102 }
103
104 #[test]
105 fn test_key_matching() {
106 let full = QueryKey::new("users").with("id", 123);
107 let pattern = QueryKey::new("users");
108
109 assert!(full.matches(&pattern));
110 assert!(!pattern.matches(&full));
111 }
112}