skp_cache_core/traits/
key.rs

1//! Cache key trait and implementations
2
3use std::fmt::Display;
4
5/// Trait for types that can be used as cache keys
6///
7/// Implement this trait to use custom types as cache keys.
8pub trait CacheKey: Send + Sync {
9    /// Generate the key string
10    fn cache_key(&self) -> String;
11
12    /// Optional namespace for the key
13    fn namespace(&self) -> Option<&str> {
14        None
15    }
16
17    /// Get the full key including namespace
18    fn full_key(&self) -> String {
19        match self.namespace() {
20            Some(ns) => format!("{}:{}", ns, self.cache_key()),
21            None => self.cache_key(),
22        }
23    }
24}
25
26// Implementations for common types
27
28impl CacheKey for String {
29    fn cache_key(&self) -> String {
30        self.clone()
31    }
32}
33
34impl CacheKey for &str {
35    fn cache_key(&self) -> String {
36        self.to_string()
37    }
38}
39
40impl CacheKey for &String {
41    fn cache_key(&self) -> String {
42        (*self).clone()
43    }
44}
45
46// Tuple implementations for composite keys
47
48impl<T: Display + Send + Sync> CacheKey for (T,) {
49    fn cache_key(&self) -> String {
50        self.0.to_string()
51    }
52}
53
54impl<T1: Display + Send + Sync, T2: Display + Send + Sync> CacheKey for (T1, T2) {
55    fn cache_key(&self) -> String {
56        format!("{}:{}", self.0, self.1)
57    }
58}
59
60impl<T1: Display + Send + Sync, T2: Display + Send + Sync, T3: Display + Send + Sync> CacheKey
61    for (T1, T2, T3)
62{
63    fn cache_key(&self) -> String {
64        format!("{}:{}:{}", self.0, self.1, self.2)
65    }
66}
67
68impl<
69        T1: Display + Send + Sync,
70        T2: Display + Send + Sync,
71        T3: Display + Send + Sync,
72        T4: Display + Send + Sync,
73    > CacheKey for (T1, T2, T3, T4)
74{
75    fn cache_key(&self) -> String {
76        format!("{}:{}:{}:{}", self.0, self.1, self.2, self.3)
77    }
78}
79
80/// Composite key builder for complex keys
81#[derive(Debug, Clone)]
82pub struct CompositeKey {
83    parts: Vec<String>,
84    ns: Option<String>,
85}
86
87impl CompositeKey {
88    /// Create a new composite key builder
89    pub fn new() -> Self {
90        Self {
91            parts: Vec::new(),
92            ns: None,
93        }
94    }
95
96    /// Set the namespace
97    pub fn with_namespace(mut self, ns: impl Into<String>) -> Self {
98        self.ns = Some(ns.into());
99        self
100    }
101
102    /// Add a part to the key
103    pub fn part(mut self, part: impl Display) -> Self {
104        self.parts.push(part.to_string());
105        self
106    }
107
108    /// Add multiple parts
109    pub fn parts<I, S>(mut self, parts: I) -> Self
110    where
111        I: IntoIterator<Item = S>,
112        S: Display,
113    {
114        self.parts.extend(parts.into_iter().map(|p| p.to_string()));
115        self
116    }
117
118    /// Get the namespace
119    pub fn get_namespace(&self) -> Option<&str> {
120        self.ns.as_deref()
121    }
122}
123
124impl Default for CompositeKey {
125    fn default() -> Self {
126        Self::new()
127    }
128}
129
130impl CacheKey for CompositeKey {
131    fn cache_key(&self) -> String {
132        self.parts.join(":")
133    }
134
135    fn namespace(&self) -> Option<&str> {
136        self.ns.as_deref()
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_string_key() {
146        let key = "my_key".to_string();
147        assert_eq!(key.cache_key(), "my_key");
148        assert_eq!(key.full_key(), "my_key");
149    }
150
151    #[test]
152    fn test_str_key() {
153        let key = "my_key";
154        assert_eq!(key.cache_key(), "my_key");
155    }
156
157    #[test]
158    fn test_tuple_key_2() {
159        let key = ("user", 123);
160        assert_eq!(key.cache_key(), "user:123");
161    }
162
163    #[test]
164    fn test_tuple_key_3() {
165        let key = ("org", 1, "user");
166        assert_eq!(key.cache_key(), "org:1:user");
167    }
168
169    #[test]
170    fn test_composite_key() {
171        let key = CompositeKey::new()
172            .with_namespace("myapp")
173            .part("user")
174            .part(123);
175
176        assert_eq!(key.cache_key(), "user:123");
177        assert_eq!(key.get_namespace(), Some("myapp"));
178        assert_eq!(key.full_key(), "myapp:user:123");
179    }
180
181    #[test]
182    fn test_composite_key_no_namespace() {
183        let key = CompositeKey::new().part("session").part("abc123");
184
185        assert_eq!(key.cache_key(), "session:abc123");
186        assert_eq!(key.full_key(), "session:abc123");
187    }
188}