use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QueryKey {
segments: Vec<KeySegment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum KeySegment {
Static(String),
Dynamic(String, String), }
impl QueryKey {
pub fn new(root: impl Into<String>) -> Self {
Self {
segments: vec![KeySegment::Static(root.into())],
}
}
pub fn segment(mut self, segment: impl Into<String>) -> Self {
self.segments.push(KeySegment::Static(segment.into()));
self
}
pub fn with(mut self, name: impl Into<String>, value: impl ToString) -> Self {
self.segments
.push(KeySegment::Dynamic(name.into(), value.to_string()));
self
}
pub fn matches(&self, pattern: &QueryKey) -> bool {
if pattern.segments.len() > self.segments.len() {
return false;
}
self.segments
.iter()
.zip(pattern.segments.iter())
.all(|(a, b)| a == b)
}
pub fn cache_key(&self) -> String {
self.segments
.iter()
.map(|s| match s {
KeySegment::Static(v) => v.clone(),
KeySegment::Dynamic(k, v) => format!("{}={}", k, v),
})
.collect::<Vec<_>>()
.join("::")
}
}
impl Hash for QueryKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.cache_key().hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_key() {
let key = QueryKey::new("users");
assert_eq!(key.cache_key(), "users");
}
#[test]
fn test_key_with_params() {
let key = QueryKey::new("users").with("id", 123);
assert_eq!(key.cache_key(), "users::id=123");
}
#[test]
fn test_hierarchical_key() {
let key = QueryKey::new("users").segment("posts").with("id", 456);
assert_eq!(key.cache_key(), "users::posts::id=456");
}
#[test]
fn test_key_matching() {
let full = QueryKey::new("users").with("id", 123);
let pattern = QueryKey::new("users");
assert!(full.matches(&pattern));
assert!(!pattern.matches(&full));
}
}