sieve_cache/
sync.rs

1use crate::SieveCache;
2use std::borrow::Borrow;
3use std::fmt;
4use std::hash::Hash;
5use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
6
7/// A thread-safe wrapper around `SieveCache`.
8///
9/// This provides a thread-safe implementation of the SIEVE cache algorithm by wrapping
10/// the standard `SieveCache` in an `Arc<Mutex<>>`. It offers the same functionality as
11/// the underlying cache but with thread safety guarantees.
12///
13/// # Thread Safety
14///
15/// All operations acquire a lock on the entire cache, which provides strong consistency
16/// but may become a bottleneck under high contention. For workloads with high concurrency,
17/// consider using [`ShardedSieveCache`](crate::ShardedSieveCache) instead, which partitions
18/// the cache into multiple independently-locked segments.
19///
20/// # Examples
21///
22/// ```
23/// # use sieve_cache::SyncSieveCache;
24/// let cache = SyncSieveCache::new(100).unwrap();
25///
26/// // Multiple threads can safely access the cache
27/// cache.insert("key1".to_string(), "value1".to_string());
28/// assert_eq!(cache.get(&"key1".to_string()), Some("value1".to_string()));
29/// ```
30#[derive(Clone)]
31pub struct SyncSieveCache<K, V>
32where
33    K: Eq + Hash + Clone + Send + Sync,
34    V: Send + Sync,
35{
36    inner: Arc<Mutex<SieveCache<K, V>>>,
37}
38
39unsafe impl<K, V> Sync for SyncSieveCache<K, V>
40where
41    K: Eq + Hash + Clone + Send + Sync,
42    V: Send + Sync,
43{
44}
45
46impl<K, V> Default for SyncSieveCache<K, V>
47where
48    K: Eq + Hash + Clone + Send + Sync,
49    V: Send + Sync,
50{
51    /// Creates a new cache with a default capacity of 100 entries.
52    ///
53    /// # Panics
54    ///
55    /// Panics if the underlying `SieveCache::new()` returns an error, which should never
56    /// happen for a non-zero capacity.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// # use sieve_cache::SyncSieveCache;
62    /// # use std::default::Default;
63    /// let cache: SyncSieveCache<String, u32> = Default::default();
64    /// assert_eq!(cache.capacity(), 100);
65    /// ```
66    fn default() -> Self {
67        Self::new(100).expect("Failed to create cache with default capacity")
68    }
69}
70
71impl<K, V> fmt::Debug for SyncSieveCache<K, V>
72where
73    K: Eq + Hash + Clone + Send + Sync + fmt::Debug,
74    V: Send + Sync + fmt::Debug,
75{
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        let guard = self.locked_cache();
78        f.debug_struct("SyncSieveCache")
79            .field("capacity", &guard.capacity())
80            .field("len", &guard.len())
81            .finish()
82    }
83}
84
85impl<K, V> From<SieveCache<K, V>> for SyncSieveCache<K, V>
86where
87    K: Eq + Hash + Clone + Send + Sync,
88    V: Send + Sync,
89{
90    /// Creates a new thread-safe cache from an existing `SieveCache`.
91    ///
92    /// This allows for easily converting a single-threaded cache to a thread-safe version.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use sieve_cache::{SieveCache, SyncSieveCache};
98    /// let mut single_threaded = SieveCache::new(100).unwrap();
99    /// single_threaded.insert("key".to_string(), "value".to_string());
100    ///
101    /// // Convert to thread-safe version
102    /// let thread_safe = SyncSieveCache::from(single_threaded);
103    /// assert_eq!(thread_safe.get(&"key".to_string()), Some("value".to_string()));
104    /// ```
105    fn from(cache: SieveCache<K, V>) -> Self {
106        Self {
107            inner: Arc::new(Mutex::new(cache)),
108        }
109    }
110}
111
112impl<K, V> IntoIterator for SyncSieveCache<K, V>
113where
114    K: Eq + Hash + Clone + Send + Sync,
115    V: Clone + Send + Sync,
116{
117    type Item = (K, V);
118    type IntoIter = std::vec::IntoIter<(K, V)>;
119
120    /// Converts the cache into an iterator over its key-value pairs.
121    ///
122    /// This collects all entries into a Vec and returns an iterator over that Vec.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// # use sieve_cache::SyncSieveCache;
128    /// # use std::collections::HashMap;
129    /// let cache = SyncSieveCache::new(100).unwrap();
130    /// cache.insert("key1".to_string(), "value1".to_string());
131    /// cache.insert("key2".to_string(), "value2".to_string());
132    ///
133    /// // Collect into a HashMap
134    /// let map: HashMap<_, _> = cache.into_iter().collect();
135    /// assert_eq!(map.len(), 2);
136    /// assert_eq!(map.get("key1"), Some(&"value1".to_string()));
137    /// ```
138    fn into_iter(self) -> Self::IntoIter {
139        self.entries().into_iter()
140    }
141}
142
143impl<K, V> SyncSieveCache<K, V>
144where
145    K: Eq + Hash + Clone + Send + Sync,
146    V: Send + Sync,
147{
148    /// Creates a new thread-safe cache with the given capacity.
149    ///
150    /// # Errors
151    ///
152    /// Returns an error if the capacity is zero.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// # use sieve_cache::SyncSieveCache;
158    /// let cache = SyncSieveCache::<String, String>::new(100).unwrap();
159    /// ```
160    pub fn new(capacity: usize) -> Result<Self, &'static str> {
161        let cache = SieveCache::new(capacity)?;
162        Ok(Self {
163            inner: Arc::new(Mutex::new(cache)),
164        })
165    }
166
167    /// Returns a locked reference to the underlying cache.
168    ///
169    /// This is an internal helper method to abstract away the lock handling.
170    /// If the mutex is poisoned due to a panic in another thread, the poison
171    /// error is recovered from by calling `into_inner()` to access the underlying data.
172    #[inline]
173    fn locked_cache(&self) -> MutexGuard<'_, SieveCache<K, V>> {
174        self.inner.lock().unwrap_or_else(PoisonError::into_inner)
175    }
176
177    /// Returns the capacity of the cache.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// # use sieve_cache::SyncSieveCache;
183    /// let cache = SyncSieveCache::<String, u32>::new(100).unwrap();
184    /// assert_eq!(cache.capacity(), 100);
185    /// ```
186    pub fn capacity(&self) -> usize {
187        self.locked_cache().capacity()
188    }
189
190    /// Returns the number of cached values.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// # use sieve_cache::SyncSieveCache;
196    /// let cache = SyncSieveCache::new(100).unwrap();
197    /// cache.insert("key".to_string(), "value".to_string());
198    /// assert_eq!(cache.len(), 1);
199    /// ```
200    pub fn len(&self) -> usize {
201        self.locked_cache().len()
202    }
203
204    /// Returns `true` when no values are currently cached.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// # use sieve_cache::SyncSieveCache;
210    /// let cache = SyncSieveCache::<String, String>::new(100).unwrap();
211    /// assert!(cache.is_empty());
212    ///
213    /// cache.insert("key".to_string(), "value".to_string());
214    /// assert!(!cache.is_empty());
215    /// ```
216    pub fn is_empty(&self) -> bool {
217        self.locked_cache().is_empty()
218    }
219
220    /// Returns `true` if there is a value in the cache mapped to by `key`.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// # use sieve_cache::SyncSieveCache;
226    /// let cache = SyncSieveCache::new(100).unwrap();
227    /// cache.insert("key".to_string(), "value".to_string());
228    ///
229    /// assert!(cache.contains_key(&"key".to_string()));
230    /// assert!(!cache.contains_key(&"missing".to_string()));
231    /// ```
232    pub fn contains_key<Q>(&self, key: &Q) -> bool
233    where
234        Q: Hash + Eq + ?Sized,
235        K: Borrow<Q>,
236    {
237        let mut guard = self.locked_cache();
238        guard.contains_key(key)
239    }
240
241    /// Gets a clone of the value in the cache mapped to by `key`.
242    ///
243    /// If no value exists for `key`, this returns `None`.
244    ///
245    /// # Note
246    ///
247    /// Unlike the unwrapped `SieveCache`, this returns a clone of the value
248    /// rather than a reference, since the mutex guard would be dropped after
249    /// this method returns. This means that `V` must implement `Clone`.
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// # use sieve_cache::SyncSieveCache;
255    /// let cache = SyncSieveCache::new(100).unwrap();
256    /// cache.insert("key".to_string(), "value".to_string());
257    ///
258    /// assert_eq!(cache.get(&"key".to_string()), Some("value".to_string()));
259    /// assert_eq!(cache.get(&"missing".to_string()), None);
260    /// ```
261    pub fn get<Q>(&self, key: &Q) -> Option<V>
262    where
263        Q: Hash + Eq + ?Sized,
264        K: Borrow<Q>,
265        V: Clone,
266    {
267        let mut guard = self.locked_cache();
268        guard.get(key).cloned()
269    }
270
271    /// Gets a mutable reference to the value in the cache mapped to by `key` via a callback function.
272    ///
273    /// If no value exists for `key`, the callback will not be invoked and this returns `false`.
274    /// Otherwise, the callback is invoked with a mutable reference to the value and this returns `true`.
275    ///
276    /// This operation marks the entry as "visited" in the SIEVE algorithm,
277    /// which affects eviction decisions.
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// # use sieve_cache::SyncSieveCache;
283    /// let cache = SyncSieveCache::new(100).unwrap();
284    /// cache.insert("key".to_string(), "value".to_string());
285    ///
286    /// // Modify the value in-place
287    /// cache.get_mut(&"key".to_string(), |value| {
288    ///     *value = "new_value".to_string();
289    /// });
290    ///
291    /// assert_eq!(cache.get(&"key".to_string()), Some("new_value".to_string()));
292    /// ```
293    pub fn get_mut<Q, F>(&self, key: &Q, f: F) -> bool
294    where
295        Q: Hash + Eq + ?Sized,
296        K: Borrow<Q>,
297        F: FnOnce(&mut V),
298    {
299        let mut guard = self.locked_cache();
300        if let Some(value) = guard.get_mut(key) {
301            f(value);
302            true
303        } else {
304            false
305        }
306    }
307
308    /// Maps `key` to `value` in the cache, possibly evicting old entries.
309    ///
310    /// This method returns `true` when this is a new entry, and `false` if an existing entry was
311    /// updated.
312    ///
313    /// # Examples
314    ///
315    /// ```
316    /// # use sieve_cache::SyncSieveCache;
317    /// let cache = SyncSieveCache::new(100).unwrap();
318    ///
319    /// // Insert a new key
320    /// assert!(cache.insert("key1".to_string(), "value1".to_string()));
321    ///
322    /// // Update an existing key
323    /// assert!(!cache.insert("key1".to_string(), "updated_value".to_string()));
324    /// ```
325    pub fn insert(&self, key: K, value: V) -> bool {
326        let mut guard = self.locked_cache();
327        guard.insert(key, value)
328    }
329
330    /// Removes the cache entry mapped to by `key`.
331    ///
332    /// This method returns the value removed from the cache. If `key` did not map to any value,
333    /// then this returns `None`.
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// # use sieve_cache::SyncSieveCache;
339    /// let cache = SyncSieveCache::new(100).unwrap();
340    /// cache.insert("key".to_string(), "value".to_string());
341    ///
342    /// // Remove an existing key
343    /// assert_eq!(cache.remove(&"key".to_string()), Some("value".to_string()));
344    ///
345    /// // Attempt to remove a missing key
346    /// assert_eq!(cache.remove(&"key".to_string()), None);
347    /// ```
348    pub fn remove<Q>(&self, key: &Q) -> Option<V>
349    where
350        K: Borrow<Q>,
351        Q: Eq + Hash + ?Sized,
352    {
353        let mut guard = self.locked_cache();
354        guard.remove(key)
355    }
356
357    /// Removes and returns a value from the cache that was not recently accessed.
358    ///
359    /// This implements the SIEVE eviction algorithm to select an entry for removal.
360    /// If no suitable value exists, this returns `None`.
361    ///
362    /// # Examples
363    ///
364    /// ```
365    /// # use sieve_cache::SyncSieveCache;
366    /// let cache = SyncSieveCache::new(2).unwrap();
367    /// cache.insert("key1".to_string(), "value1".to_string());
368    /// cache.insert("key2".to_string(), "value2".to_string());
369    ///
370    /// // Accessing key1 marks it as recently used
371    /// assert_eq!(cache.get(&"key1".to_string()), Some("value1".to_string()));
372    ///
373    /// // Insert a new key, which should evict key2
374    /// cache.insert("key3".to_string(), "value3".to_string());
375    ///
376    /// // key2 should have been evicted
377    /// assert_eq!(cache.get(&"key2".to_string()), None);
378    /// ```
379    pub fn evict(&self) -> Option<V> {
380        let mut guard = self.locked_cache();
381        guard.evict()
382    }
383
384    /// Removes all entries from the cache.
385    ///
386    /// This operation clears all stored values and resets the cache to an empty state,
387    /// while maintaining the original capacity.
388    ///
389    /// # Examples
390    ///
391    /// ```
392    /// # use sieve_cache::SyncSieveCache;
393    /// let cache = SyncSieveCache::new(100).unwrap();
394    /// cache.insert("key1".to_string(), "value1".to_string());
395    /// cache.insert("key2".to_string(), "value2".to_string());
396    ///
397    /// assert_eq!(cache.len(), 2);
398    ///
399    /// cache.clear();
400    /// assert_eq!(cache.len(), 0);
401    /// assert!(cache.is_empty());
402    /// ```
403    pub fn clear(&self) {
404        let mut guard = self.locked_cache();
405        guard.clear();
406    }
407
408    /// Returns an iterator over all keys in the cache.
409    ///
410    /// The order of keys is not specified and should not be relied upon.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// # use sieve_cache::SyncSieveCache;
416    /// # use std::collections::HashSet;
417    /// let cache = SyncSieveCache::new(100).unwrap();
418    /// cache.insert("key1".to_string(), "value1".to_string());
419    /// cache.insert("key2".to_string(), "value2".to_string());
420    ///
421    /// let keys: HashSet<_> = cache.keys().into_iter().collect();
422    /// assert_eq!(keys.len(), 2);
423    /// assert!(keys.contains(&"key1".to_string()));
424    /// assert!(keys.contains(&"key2".to_string()));
425    /// ```
426    pub fn keys(&self) -> Vec<K> {
427        let guard = self.locked_cache();
428        guard.keys().cloned().collect()
429    }
430
431    /// Returns all values in the cache.
432    ///
433    /// The order of values is not specified and should not be relied upon.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// # use sieve_cache::SyncSieveCache;
439    /// # use std::collections::HashSet;
440    /// let cache = SyncSieveCache::new(100).unwrap();
441    /// cache.insert("key1".to_string(), "value1".to_string());
442    /// cache.insert("key2".to_string(), "value2".to_string());
443    ///
444    /// let values: HashSet<_> = cache.values().into_iter().collect();
445    /// assert_eq!(values.len(), 2);
446    /// assert!(values.contains(&"value1".to_string()));
447    /// assert!(values.contains(&"value2".to_string()));
448    /// ```
449    pub fn values(&self) -> Vec<V>
450    where
451        V: Clone,
452    {
453        let guard = self.locked_cache();
454        guard.values().cloned().collect()
455    }
456
457    /// Returns all key-value pairs in the cache.
458    ///
459    /// The order of pairs is not specified and should not be relied upon.
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// # use sieve_cache::SyncSieveCache;
465    /// # use std::collections::HashMap;
466    /// let cache = SyncSieveCache::new(100).unwrap();
467    /// cache.insert("key1".to_string(), "value1".to_string());
468    /// cache.insert("key2".to_string(), "value2".to_string());
469    ///
470    /// let entries: HashMap<_, _> = cache.entries().into_iter().collect();
471    /// assert_eq!(entries.len(), 2);
472    /// assert_eq!(entries.get(&"key1".to_string()), Some(&"value1".to_string()));
473    /// assert_eq!(entries.get(&"key2".to_string()), Some(&"value2".to_string()));
474    /// ```
475    pub fn entries(&self) -> Vec<(K, V)>
476    where
477        V: Clone,
478    {
479        let guard = self.locked_cache();
480        guard.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
481    }
482
483    /// Applies a function to all values in the cache.
484    ///
485    /// This method marks all entries as visited.
486    ///
487    /// # Examples
488    ///
489    /// ```
490    /// # use sieve_cache::SyncSieveCache;
491    /// let cache = SyncSieveCache::new(100).unwrap();
492    /// cache.insert("key1".to_string(), "value1".to_string());
493    /// cache.insert("key2".to_string(), "value2".to_string());
494    ///
495    /// // Update all values by appending text
496    /// cache.for_each_value(|value| {
497    ///     *value = format!("{}_updated", value);
498    /// });
499    ///
500    /// assert_eq!(cache.get(&"key1".to_string()), Some("value1_updated".to_string()));
501    /// assert_eq!(cache.get(&"key2".to_string()), Some("value2_updated".to_string()));
502    /// ```
503    pub fn for_each_value<F>(&self, f: F)
504    where
505        F: FnMut(&mut V),
506    {
507        let mut guard = self.locked_cache();
508        guard.values_mut().for_each(f);
509    }
510
511    /// Applies a function to all key-value pairs in the cache.
512    ///
513    /// This method marks all entries as visited.
514    ///
515    /// # Examples
516    ///
517    /// ```
518    /// # use sieve_cache::SyncSieveCache;
519    /// let cache = SyncSieveCache::new(100).unwrap();
520    /// cache.insert("key1".to_string(), "value1".to_string());
521    /// cache.insert("key2".to_string(), "value2".to_string());
522    ///
523    /// // Update all values associated with keys containing '1'
524    /// cache.for_each_entry(|(key, value)| {
525    ///     if key.contains('1') {
526    ///         *value = format!("{}_special", value);
527    ///     }
528    /// });
529    ///
530    /// assert_eq!(cache.get(&"key1".to_string()), Some("value1_special".to_string()));
531    /// assert_eq!(cache.get(&"key2".to_string()), Some("value2".to_string()));
532    /// ```
533    pub fn for_each_entry<F>(&self, f: F)
534    where
535        F: FnMut((&K, &mut V)),
536    {
537        let mut guard = self.locked_cache();
538        guard.iter_mut().for_each(f);
539    }
540
541    /// Gets exclusive access to the underlying cache to perform multiple operations atomically.
542    ///
543    /// This is useful when you need to perform a series of operations that depend on each other
544    /// and you want to ensure that no other thread can access the cache between operations.
545    ///
546    /// # Examples
547    ///
548    /// ```
549    /// # use sieve_cache::SyncSieveCache;
550    /// let cache = SyncSieveCache::new(100).unwrap();
551    ///
552    /// cache.with_lock(|inner_cache| {
553    ///     // Perform multiple operations atomically
554    ///     inner_cache.insert("key1".to_string(), "value1".to_string());
555    ///     inner_cache.insert("key2".to_string(), "value2".to_string());
556    ///
557    ///     // We can check internal state mid-transaction
558    ///     assert_eq!(inner_cache.len(), 2);
559    /// });
560    /// ```
561    pub fn with_lock<F, T>(&self, f: F) -> T
562    where
563        F: FnOnce(&mut SieveCache<K, V>) -> T,
564    {
565        let mut guard = self.locked_cache();
566        f(&mut guard)
567    }
568
569    /// Retains only the elements specified by the predicate.
570    ///
571    /// Removes all entries for which the provided function returns `false`.
572    /// The elements are visited in arbitrary, unspecified order.
573    /// This operation acquires a lock on the entire cache.
574    ///
575    /// # Examples
576    ///
577    /// ```
578    /// # use sieve_cache::SyncSieveCache;
579    /// let cache = SyncSieveCache::new(100).unwrap();
580    /// cache.insert("key1".to_string(), 100);
581    /// cache.insert("key2".to_string(), 200);
582    /// cache.insert("key3".to_string(), 300);
583    ///
584    /// // Keep only entries with values greater than 150
585    /// cache.retain(|_, value| *value > 150);
586    ///
587    /// assert_eq!(cache.len(), 2);
588    /// assert!(!cache.contains_key(&"key1".to_string()));
589    /// assert!(cache.contains_key(&"key2".to_string()));
590    /// assert!(cache.contains_key(&"key3".to_string()));
591    /// ```
592    pub fn retain<F>(&self, f: F)
593    where
594        F: FnMut(&K, &V) -> bool,
595    {
596        let mut guard = self.locked_cache();
597        guard.retain(f);
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use super::*;
604    use std::thread;
605
606    #[test]
607    fn test_sync_cache() {
608        let cache = SyncSieveCache::new(100).unwrap();
609
610        // Insert a value
611        assert!(cache.insert("key1".to_string(), "value1".to_string()));
612
613        // Read back the value
614        assert_eq!(cache.get(&"key1".to_string()), Some("value1".to_string()));
615
616        // Check contains_key
617        assert!(cache.contains_key(&"key1".to_string()));
618
619        // Check capacity and length
620        assert_eq!(cache.capacity(), 100);
621        assert_eq!(cache.len(), 1);
622
623        // Remove a value
624        assert_eq!(
625            cache.remove(&"key1".to_string()),
626            Some("value1".to_string())
627        );
628        assert_eq!(cache.len(), 0);
629        assert!(cache.is_empty());
630    }
631
632    #[test]
633    fn test_multithreaded_access() {
634        let cache = SyncSieveCache::new(100).unwrap();
635        let cache_clone = cache.clone();
636
637        // Add some initial data
638        cache.insert("shared".to_string(), "initial".to_string());
639
640        // Spawn a thread that updates the cache
641        let thread = thread::spawn(move || {
642            cache_clone.insert("shared".to_string(), "updated".to_string());
643            cache_clone.insert("thread_only".to_string(), "thread_value".to_string());
644        });
645
646        // Main thread operations
647        cache.insert("main_only".to_string(), "main_value".to_string());
648
649        // Wait for thread to complete
650        thread.join().unwrap();
651
652        // Verify results
653        assert_eq!(
654            cache.get(&"shared".to_string()),
655            Some("updated".to_string())
656        );
657        assert_eq!(
658            cache.get(&"thread_only".to_string()),
659            Some("thread_value".to_string())
660        );
661        assert_eq!(
662            cache.get(&"main_only".to_string()),
663            Some("main_value".to_string())
664        );
665        assert_eq!(cache.len(), 3);
666    }
667
668    #[test]
669    fn test_with_lock() {
670        let cache = SyncSieveCache::new(100).unwrap();
671
672        // Perform multiple operations atomically
673        cache.with_lock(|inner_cache| {
674            inner_cache.insert("key1".to_string(), "value1".to_string());
675            inner_cache.insert("key2".to_string(), "value2".to_string());
676            inner_cache.insert("key3".to_string(), "value3".to_string());
677        });
678
679        assert_eq!(cache.len(), 3);
680    }
681
682    #[test]
683    fn test_get_mut() {
684        let cache = SyncSieveCache::new(100).unwrap();
685        cache.insert("key".to_string(), "value".to_string());
686
687        // Modify the value in-place
688        let modified = cache.get_mut(&"key".to_string(), |value| {
689            *value = "new_value".to_string();
690        });
691        assert!(modified);
692
693        // Verify the value was updated
694        assert_eq!(cache.get(&"key".to_string()), Some("new_value".to_string()));
695
696        // Try to modify a non-existent key
697        let modified = cache.get_mut(&"missing".to_string(), |_| {
698            panic!("This should not be called");
699        });
700        assert!(!modified);
701    }
702
703    #[test]
704    fn test_clear() {
705        let cache = SyncSieveCache::new(10).unwrap();
706        cache.insert("key1".to_string(), "value1".to_string());
707        cache.insert("key2".to_string(), "value2".to_string());
708
709        assert_eq!(cache.len(), 2);
710        assert!(!cache.is_empty());
711
712        cache.clear();
713
714        assert_eq!(cache.len(), 0);
715        assert!(cache.is_empty());
716        assert_eq!(cache.get(&"key1".to_string()), None);
717        assert_eq!(cache.get(&"key2".to_string()), None);
718    }
719
720    #[test]
721    fn test_keys_values_entries() {
722        let cache = SyncSieveCache::new(10).unwrap();
723        cache.insert("key1".to_string(), "value1".to_string());
724        cache.insert("key2".to_string(), "value2".to_string());
725
726        // Test keys
727        let keys = cache.keys();
728        assert_eq!(keys.len(), 2);
729        assert!(keys.contains(&"key1".to_string()));
730        assert!(keys.contains(&"key2".to_string()));
731
732        // Test values
733        let values = cache.values();
734        assert_eq!(values.len(), 2);
735        assert!(values.contains(&"value1".to_string()));
736        assert!(values.contains(&"value2".to_string()));
737
738        // Test entries
739        let entries = cache.entries();
740        assert_eq!(entries.len(), 2);
741        assert!(entries.contains(&("key1".to_string(), "value1".to_string())));
742        assert!(entries.contains(&("key2".to_string(), "value2".to_string())));
743    }
744
745    #[test]
746    fn test_for_each_methods() {
747        let cache = SyncSieveCache::new(10).unwrap();
748        cache.insert("key1".to_string(), "value1".to_string());
749        cache.insert("key2".to_string(), "value2".to_string());
750
751        // Test for_each_value
752        cache.for_each_value(|value| {
753            *value = format!("{}_updated", value);
754        });
755
756        assert_eq!(
757            cache.get(&"key1".to_string()),
758            Some("value1_updated".to_string())
759        );
760        assert_eq!(
761            cache.get(&"key2".to_string()),
762            Some("value2_updated".to_string())
763        );
764
765        // Test for_each_entry
766        cache.for_each_entry(|(key, value)| {
767            if key == "key1" {
768                *value = format!("{}_special", value);
769            }
770        });
771
772        assert_eq!(
773            cache.get(&"key1".to_string()),
774            Some("value1_updated_special".to_string())
775        );
776        assert_eq!(
777            cache.get(&"key2".to_string()),
778            Some("value2_updated".to_string())
779        );
780    }
781
782    #[test]
783    fn test_retain() {
784        let cache = SyncSieveCache::new(10).unwrap();
785
786        // Add some entries
787        cache.insert("even1".to_string(), 2);
788        cache.insert("even2".to_string(), 4);
789        cache.insert("odd1".to_string(), 1);
790        cache.insert("odd2".to_string(), 3);
791
792        assert_eq!(cache.len(), 4);
793
794        // Keep only entries with even values
795        cache.retain(|_, v| v % 2 == 0);
796
797        assert_eq!(cache.len(), 2);
798        assert!(cache.contains_key(&"even1".to_string()));
799        assert!(cache.contains_key(&"even2".to_string()));
800        assert!(!cache.contains_key(&"odd1".to_string()));
801        assert!(!cache.contains_key(&"odd2".to_string()));
802
803        // Keep only entries with keys containing '1'
804        cache.retain(|k, _| k.contains('1'));
805
806        assert_eq!(cache.len(), 1);
807        assert!(cache.contains_key(&"even1".to_string()));
808        assert!(!cache.contains_key(&"even2".to_string()));
809    }
810}