1use crate::entity::CacheEntity;
4use crate::error::Result;
5
6pub trait CacheFeed<T: CacheEntity>: Send {
44 fn entity_id(&mut self) -> T::Key;
48
49 fn feed(&mut self, entity: Option<T>);
54
55 fn validate(&self) -> Result<()> {
60 Ok(())
61 }
62
63 fn on_loaded(&mut self, _entity: &T) -> Result<()> {
67 Ok(())
68 }
69
70 fn on_miss(&mut self, _key: &str) -> Result<()> {
74 Ok(())
75 }
76
77 fn on_hit(&mut self, _key: &str) -> Result<()> {
81 Ok(())
82 }
83}
84
85pub struct GenericFeeder<T: CacheEntity> {
91 pub id: T::Key,
92 pub data: Option<T>,
93}
94
95impl<T: CacheEntity> GenericFeeder<T> {
96 pub fn new(id: T::Key) -> Self {
97 GenericFeeder { id, data: None }
98 }
99}
100
101impl<T: CacheEntity> CacheFeed<T> for GenericFeeder<T> {
102 fn entity_id(&mut self) -> T::Key {
103 self.id.clone()
104 }
105
106 fn feed(&mut self, entity: Option<T>) {
107 self.data = entity;
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use serde::{Deserialize, Serialize};
115
116 #[derive(Clone, Serialize, Deserialize)]
117 struct TestEntity {
118 id: String,
119 value: String,
120 }
121
122 impl CacheEntity for TestEntity {
123 type Key = String;
124
125 fn cache_key(&self) -> Self::Key {
126 self.id.clone()
127 }
128
129 fn cache_prefix() -> &'static str {
130 "test"
131 }
132 }
133
134 #[test]
135 fn test_generic_feeder() {
136 let mut feeder = GenericFeeder::new("test_id".to_string());
137
138 assert_eq!(feeder.entity_id(), "test_id");
139
140 let entity = TestEntity {
141 id: "test_id".to_string(),
142 value: "data".to_string(),
143 };
144
145 feeder.feed(Some(entity.clone()));
146 assert!(feeder.data.is_some());
147 }
148
149 #[test]
150 fn test_feeder_validation() {
151 let feeder: GenericFeeder<TestEntity> = GenericFeeder::new("id".to_string());
152 assert!(feeder.validate().is_ok());
153 }
154
155 #[test]
156 fn test_feeder_on_loaded_hook() {
157 use std::sync::{Arc, Mutex};
158
159 #[derive(Clone)]
160 struct TrackingFeeder {
161 id: String,
162 data: Option<TestEntity>,
163 loaded_count: Arc<Mutex<usize>>,
164 }
165
166 impl CacheFeed<TestEntity> for TrackingFeeder {
167 fn entity_id(&mut self) -> String {
168 self.id.clone()
169 }
170
171 fn feed(&mut self, entity: Option<TestEntity>) {
172 self.data = entity;
173 }
174
175 fn on_loaded(&mut self, _entity: &TestEntity) -> Result<()> {
176 *self.loaded_count.lock().unwrap() += 1;
177 Ok(())
178 }
179 }
180
181 let loaded_count = Arc::new(Mutex::new(0));
182 let mut feeder = TrackingFeeder {
183 id: "1".to_string(),
184 data: None,
185 loaded_count: loaded_count.clone(),
186 };
187
188 let entity = TestEntity {
189 id: "1".to_string(),
190 value: "test".to_string(),
191 };
192
193 feeder.on_loaded(&entity).unwrap();
194 assert_eq!(*loaded_count.lock().unwrap(), 1);
195 }
196
197 #[test]
198 fn test_feeder_on_hit_hook() {
199 use std::sync::{Arc, Mutex};
200
201 #[derive(Clone)]
202 struct HitTrackingFeeder {
203 id: String,
204 data: Option<TestEntity>,
205 hit_keys: Arc<Mutex<Vec<String>>>,
206 }
207
208 impl CacheFeed<TestEntity> for HitTrackingFeeder {
209 fn entity_id(&mut self) -> String {
210 self.id.clone()
211 }
212
213 fn feed(&mut self, entity: Option<TestEntity>) {
214 self.data = entity;
215 }
216
217 fn on_hit(&mut self, key: &str) -> Result<()> {
218 self.hit_keys.lock().unwrap().push(key.to_string());
219 Ok(())
220 }
221 }
222
223 let hit_keys = Arc::new(Mutex::new(Vec::new()));
224 let mut feeder = HitTrackingFeeder {
225 id: "1".to_string(),
226 data: None,
227 hit_keys: hit_keys.clone(),
228 };
229
230 feeder.on_hit("test:1").unwrap();
231 assert_eq!(hit_keys.lock().unwrap().len(), 1);
232 assert_eq!(hit_keys.lock().unwrap()[0], "test:1");
233 }
234
235 #[test]
236 fn test_feeder_on_miss_hook() {
237 use std::sync::{Arc, Mutex};
238
239 #[derive(Clone)]
240 struct MissTrackingFeeder {
241 id: String,
242 data: Option<TestEntity>,
243 miss_keys: Arc<Mutex<Vec<String>>>,
244 }
245
246 impl CacheFeed<TestEntity> for MissTrackingFeeder {
247 fn entity_id(&mut self) -> String {
248 self.id.clone()
249 }
250
251 fn feed(&mut self, entity: Option<TestEntity>) {
252 self.data = entity;
253 }
254
255 fn on_miss(&mut self, key: &str) -> Result<()> {
256 self.miss_keys.lock().unwrap().push(key.to_string());
257 Ok(())
258 }
259 }
260
261 let miss_keys = Arc::new(Mutex::new(Vec::new()));
262 let mut feeder = MissTrackingFeeder {
263 id: "1".to_string(),
264 data: None,
265 miss_keys: miss_keys.clone(),
266 };
267
268 feeder.on_miss("test:1").unwrap();
269 assert_eq!(miss_keys.lock().unwrap().len(), 1);
270 assert_eq!(miss_keys.lock().unwrap()[0], "test:1");
271 }
272
273 #[test]
274 fn test_generic_feeder_feed_none() {
275 let mut feeder: GenericFeeder<TestEntity> = GenericFeeder::new("test_id".to_string());
276 feeder.feed(None);
277 assert!(feeder.data.is_none());
278 }
279}