1use crate::entity::CacheEntity;
4
5type KeyGeneratorFn = dyn Fn(&dyn std::fmt::Display) -> String + Send + Sync;
7
8pub struct CacheKeyBuilder;
10
11impl CacheKeyBuilder {
12 pub fn build<T: CacheEntity>(id: &T::Key) -> String {
14 format!("{}:{}", T::cache_prefix(), id)
15 }
16
17 pub fn build_with_prefix(prefix: &str, id: &dyn std::fmt::Display) -> String {
19 format!("{}:{}", prefix, id)
20 }
21
22 pub fn build_composite(parts: &[&str]) -> String {
24 parts.join(":")
25 }
26
27 pub fn parse(key: &str) -> Vec<&str> {
29 key.split(':').collect()
30 }
31}
32
33pub struct KeyRegistry {
35 generators: std::collections::HashMap<String, Box<KeyGeneratorFn>>,
36}
37
38impl KeyRegistry {
39 pub fn new() -> Self {
40 KeyRegistry {
41 generators: std::collections::HashMap::new(),
42 }
43 }
44
45 pub fn register<F>(&mut self, type_name: String, generator: F)
47 where
48 F: Fn(&dyn std::fmt::Display) -> String + Send + Sync + 'static,
49 {
50 self.generators.insert(type_name, Box::new(generator));
51 }
52
53 pub fn get(&self, type_name: &str) -> Option<&KeyGeneratorFn> {
55 self.generators.get(type_name).map(|b| b.as_ref())
56 }
57
58 pub fn generate(&self, type_name: &str, id: &dyn std::fmt::Display) -> Option<String> {
60 self.get(type_name).map(|gen| gen(id))
61 }
62}
63
64impl Default for KeyRegistry {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use serde::{Deserialize, Serialize};
74
75 #[derive(Clone, Serialize, Deserialize)]
76 struct TestEntity {
77 id: String,
78 }
79
80 impl CacheEntity for TestEntity {
81 type Key = String;
82
83 fn cache_key(&self) -> Self::Key {
84 self.id.clone()
85 }
86
87 fn cache_prefix() -> &'static str {
88 "test"
89 }
90 }
91
92 #[test]
93 fn test_cache_key_builder() {
94 let key = CacheKeyBuilder::build::<TestEntity>(&"entity_123".to_string());
95 assert_eq!(key, "test:entity_123");
96 }
97
98 #[test]
99 fn test_cache_key_builder_custom_prefix() {
100 let key = CacheKeyBuilder::build_with_prefix("custom", &"123");
101 assert_eq!(key, "custom:123");
102 }
103
104 #[test]
105 fn test_composite_key_builder() {
106 let key = CacheKeyBuilder::build_composite(&["user", "123", "profile"]);
107 assert_eq!(key, "user:123:profile");
108 }
109
110 #[test]
111 fn test_composite_key_parser() {
112 let key = "user:123:profile";
113 let parts = CacheKeyBuilder::parse(key);
114 assert_eq!(parts, vec!["user", "123", "profile"]);
115 }
116
117 #[test]
118 fn test_key_registry() {
119 let mut registry = KeyRegistry::new();
120
121 registry.register("custom".to_string(), |id| format!("CUSTOM_{}", id));
122
123 let generated = registry.generate("custom", &"123").unwrap();
124 assert_eq!(generated, "CUSTOM_123");
125
126 assert!(registry.generate("unknown", &"123").is_none());
127 }
128}