Skip to main content

std_ext/
map.rs

1use std::borrow::Borrow;
2use std::cmp::Ord;
3use std::collections::btree_map;
4use std::collections::hash_map;
5use std::collections::BTreeMap;
6use std::collections::HashMap;
7use std::fmt::Debug;
8use std::hash::{BuildHasher, Hash};
9use std::time::{Duration, Instant};
10
11pub trait CacheMapExt<K, V> {
12    fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
13    where
14        K: Borrow<Q> + Ord,
15        Q: Hash + Eq + Ord + ?Sized;
16
17    fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
18    where
19        K: Borrow<Q> + Ord,
20        Q: Hash + Eq + Ord + ?Sized;
21
22    fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V>;
23
24    fn remove_expired_values(&mut self);
25}
26
27pub trait EntryExt<'a, K, V> {
28    fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V;
29    fn or_insert_with_timeout_f<F: FnOnce() -> V>(
30        self,
31        default: F,
32        timeout: Option<Duration>,
33    ) -> &'a mut V;
34    fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
35        self,
36        default: F,
37        timeout: Option<Duration>,
38    ) -> &'a mut V;
39    fn and_modify_with_timeout<F>(self, f: F) -> Self
40    where
41        F: FnOnce(&mut V);
42}
43
44impl<K, V, S> CacheMapExt<K, V> for HashMap<K, TimedValue<V>, S>
45where
46    K: Eq + Hash,
47    S: BuildHasher,
48{
49    fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
50    where
51        K: Borrow<Q>,
52        Q: Hash + Eq + ?Sized,
53    {
54        self.get(k).and_then(|tv| {
55            if tv.is_expired() {
56                None
57            } else {
58                Some(tv.value())
59            }
60        })
61    }
62
63    fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
64    where
65        K: Borrow<Q>,
66        Q: Hash + Eq + ?Sized,
67    {
68        self.get_mut(k).and_then(|tv| {
69            if tv.is_expired() {
70                None
71            } else {
72                Some(tv.value_mut())
73            }
74        })
75    }
76
77    fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
78        self.insert(k, TimedValue::new(v, timeout))
79            .map(|tv| tv.into_value())
80    }
81
82    fn remove_expired_values(&mut self) {
83        self.retain(|_, tv| !tv.is_expired());
84    }
85}
86
87impl<'a, K, V> EntryExt<'a, K, V> for hash_map::Entry<'a, K, TimedValue<V>>
88where
89    K: Eq + Hash,
90{
91    fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
92        match self {
93            hash_map::Entry::Occupied(entry) => {
94                let v = entry.into_mut();
95                if v.is_expired() {
96                    *v = TimedValue::new(default, timeout);
97                }
98                v.value_mut()
99            }
100            hash_map::Entry::Vacant(entry) => {
101                entry.insert(TimedValue::new(default, timeout)).value_mut()
102            }
103        }
104    }
105
106    fn or_insert_with_timeout_f<F: FnOnce() -> V>(
107        self,
108        default: F,
109        timeout: Option<Duration>,
110    ) -> &'a mut V {
111        match self {
112            hash_map::Entry::Occupied(entry) => {
113                let v = entry.into_mut();
114                if v.is_expired() {
115                    *v = TimedValue::new(default(), timeout);
116                }
117                v.value_mut()
118            }
119            hash_map::Entry::Vacant(entry) => entry
120                .insert(TimedValue::new(default(), timeout))
121                .value_mut(),
122        }
123    }
124
125    fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
126        self,
127        default: F,
128        timeout: Option<Duration>,
129    ) -> &'a mut V {
130        match self {
131            hash_map::Entry::Occupied(entry) => {
132                let value = if entry.get().is_expired() {
133                    Some(default(entry.key()))
134                } else {
135                    None
136                };
137                let v = entry.into_mut();
138                if let Some(value) = value {
139                    *v = TimedValue::new(value, timeout);
140                }
141                v.value_mut()
142            }
143            hash_map::Entry::Vacant(entry) => {
144                let value = default(entry.key());
145                entry.insert(TimedValue::new(value, timeout)).value_mut()
146            }
147        }
148    }
149
150    fn and_modify_with_timeout<F>(self, f: F) -> Self
151    where
152        F: FnOnce(&mut V),
153    {
154        self.and_modify(|v| f(v.value_mut()))
155    }
156}
157
158impl<K: Ord, V> CacheMapExt<K, V> for BTreeMap<K, TimedValue<V>> {
159    fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
160    where
161        K: Borrow<Q>,
162        Q: Ord + ?Sized,
163    {
164        self.get(k).and_then(|tv| {
165            if tv.is_expired() {
166                None
167            } else {
168                Some(tv.value())
169            }
170        })
171    }
172
173    fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
174    where
175        K: Borrow<Q>,
176        Q: Ord + ?Sized,
177    {
178        self.get_mut(k).and_then(|tv| {
179            if tv.is_expired() {
180                None
181            } else {
182                Some(tv.value_mut())
183            }
184        })
185    }
186
187    fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
188        self.insert(k, TimedValue::new(v, timeout))
189            .map(|tv| tv.into_value())
190    }
191
192    fn remove_expired_values(&mut self) {
193        self.retain(|_, tv| !tv.is_expired());
194    }
195}
196
197impl<'a, K, V> EntryExt<'a, K, V> for btree_map::Entry<'a, K, TimedValue<V>>
198where
199    K: Ord,
200{
201    fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
202        match self {
203            btree_map::Entry::Occupied(entry) => {
204                let v = entry.into_mut();
205                if v.is_expired() {
206                    *v = TimedValue::new(default, timeout);
207                }
208                v.value_mut()
209            }
210            btree_map::Entry::Vacant(entry) => {
211                entry.insert(TimedValue::new(default, timeout)).value_mut()
212            }
213        }
214    }
215
216    fn or_insert_with_timeout_f<F: FnOnce() -> V>(
217        self,
218        default: F,
219        timeout: Option<Duration>,
220    ) -> &'a mut V {
221        match self {
222            btree_map::Entry::Occupied(entry) => {
223                let v = entry.into_mut();
224                if v.is_expired() {
225                    *v = TimedValue::new(default(), timeout);
226                }
227                v.value_mut()
228            }
229            btree_map::Entry::Vacant(entry) => entry
230                .insert(TimedValue::new(default(), timeout))
231                .value_mut(),
232        }
233    }
234
235    fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
236        self,
237        default: F,
238        timeout: Option<Duration>,
239    ) -> &'a mut V {
240        match self {
241            btree_map::Entry::Occupied(entry) => {
242                let value = if entry.get().is_expired() {
243                    Some(default(entry.key()))
244                } else {
245                    None
246                };
247                let v = entry.into_mut();
248                if let Some(value) = value {
249                    *v = TimedValue::new(value, timeout);
250                }
251                v.value_mut()
252            }
253            btree_map::Entry::Vacant(entry) => {
254                let value = default(entry.key());
255                entry.insert(TimedValue::new(value, timeout)).value_mut()
256            }
257        }
258    }
259
260    fn and_modify_with_timeout<F>(self, f: F) -> Self
261    where
262        F: FnOnce(&mut V),
263    {
264        self.and_modify(|v| f(v.value_mut()))
265    }
266}
267
268impl<K: Ord + Clone, V> CacheMapExt<K, V> for dequemap::DequeBTreeMap<K, TimedValue<V>> {
269    fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
270    where
271        K: Borrow<Q>,
272        Q: Ord + ?Sized,
273    {
274        self.get(k).and_then(|tv| {
275            if tv.is_expired() {
276                None
277            } else {
278                Some(tv.value())
279            }
280        })
281    }
282
283    fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
284    where
285        K: Borrow<Q>,
286        Q: Ord + ?Sized,
287    {
288        self.get_mut(k).and_then(|tv| {
289            if tv.is_expired() {
290                None
291            } else {
292                Some(tv.value_mut())
293            }
294        })
295    }
296
297    fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
298        self.insert(k, TimedValue::new(v, timeout))
299            .map(|tv| tv.into_value())
300    }
301
302    fn remove_expired_values(&mut self) {
303        self.retain(|_, tv| !tv.is_expired());
304    }
305}
306
307impl<'a, K, V> EntryExt<'a, K, V> for dequemap::btreemap::Entry<'a, K, TimedValue<V>>
308where
309    K: Ord + Clone,
310{
311    fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
312        match self {
313            dequemap::btreemap::Entry::Occupied(entry) => {
314                let v = entry.into_mut();
315                if v.is_expired() {
316                    *v = TimedValue::new(default, timeout);
317                }
318                v.value_mut()
319            }
320            dequemap::btreemap::Entry::Vacant(entry) => {
321                entry.insert(TimedValue::new(default, timeout)).value_mut()
322            }
323        }
324    }
325
326    fn or_insert_with_timeout_f<F: FnOnce() -> V>(
327        self,
328        default: F,
329        timeout: Option<Duration>,
330    ) -> &'a mut V {
331        match self {
332            dequemap::btreemap::Entry::Occupied(entry) => {
333                let v = entry.into_mut();
334                if v.is_expired() {
335                    *v = TimedValue::new(default(), timeout);
336                }
337                v.value_mut()
338            }
339            dequemap::btreemap::Entry::Vacant(entry) => entry
340                .insert(TimedValue::new(default(), timeout))
341                .value_mut(),
342        }
343    }
344
345    fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
346        self,
347        default: F,
348        timeout: Option<Duration>,
349    ) -> &'a mut V {
350        match self {
351            dequemap::btreemap::Entry::Occupied(entry) => {
352                let value = if entry.get().is_expired() {
353                    Some(default(entry.key()))
354                } else {
355                    None
356                };
357                let v = entry.into_mut();
358                if let Some(value) = value {
359                    *v = TimedValue::new(value, timeout);
360                }
361                v.value_mut()
362            }
363            dequemap::btreemap::Entry::Vacant(entry) => {
364                let value = default(entry.key());
365                entry.insert(TimedValue::new(value, timeout)).value_mut()
366            }
367        }
368    }
369
370    fn and_modify_with_timeout<F>(self, f: F) -> Self
371    where
372        F: FnOnce(&mut V),
373    {
374        self.and_modify(|v| f(v.value_mut()))
375    }
376}
377
378//------------------
379impl<K, V, S> CacheMapExt<K, V> for dequemap::DequeHashMap<K, TimedValue<V>, S>
380where
381    K: Hash + Eq + Ord + Clone,
382    S: BuildHasher,
383{
384    fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
385    where
386        K: Borrow<Q>,
387        Q: Hash + Eq + ?Sized,
388    {
389        self.get(k).and_then(|tv| {
390            if tv.is_expired() {
391                None
392            } else {
393                Some(tv.value())
394            }
395        })
396    }
397
398    fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
399    where
400        K: Borrow<Q>,
401        Q: Hash + Eq + ?Sized,
402    {
403        self.get_mut(k).and_then(|tv| {
404            if tv.is_expired() {
405                None
406            } else {
407                Some(tv.value_mut())
408            }
409        })
410    }
411
412    fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
413        self.insert(k, TimedValue::new(v, timeout))
414            .map(|tv| tv.into_value())
415    }
416
417    fn remove_expired_values(&mut self) {
418        self.retain(|_, tv| !tv.is_expired());
419    }
420}
421
422impl<'a, K, V, S> EntryExt<'a, K, V> for dequemap::hashmap::Entry<'a, K, TimedValue<V>, S>
423where
424    K: Eq + Hash + Clone,
425    S: BuildHasher,
426{
427    fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
428        match self {
429            dequemap::hashmap::Entry::Occupied(entry) => {
430                let v = entry.into_mut();
431                if v.is_expired() {
432                    *v = TimedValue::new(default, timeout);
433                }
434                v.value_mut()
435            }
436            dequemap::hashmap::Entry::Vacant(entry) => {
437                entry.insert(TimedValue::new(default, timeout)).value_mut()
438            }
439        }
440    }
441
442    fn or_insert_with_timeout_f<F: FnOnce() -> V>(
443        self,
444        default: F,
445        timeout: Option<Duration>,
446    ) -> &'a mut V {
447        match self {
448            dequemap::hashmap::Entry::Occupied(entry) => {
449                let v = entry.into_mut();
450                if v.is_expired() {
451                    *v = TimedValue::new(default(), timeout);
452                }
453                v.value_mut()
454            }
455            dequemap::hashmap::Entry::Vacant(entry) => entry
456                .insert(TimedValue::new(default(), timeout))
457                .value_mut(),
458        }
459    }
460
461    fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
462        self,
463        default: F,
464        timeout: Option<Duration>,
465    ) -> &'a mut V {
466        match self {
467            dequemap::hashmap::Entry::Occupied(entry) => {
468                let value = if entry.get().is_expired() {
469                    Some(default(entry.key()))
470                } else {
471                    None
472                };
473                let v = entry.into_mut();
474                if let Some(value) = value {
475                    *v = TimedValue::new(value, timeout);
476                }
477                v.value_mut()
478            }
479            dequemap::hashmap::Entry::Vacant(entry) => {
480                let value = default(entry.key());
481                entry.insert(TimedValue::new(value, timeout)).value_mut()
482            }
483        }
484    }
485
486    fn and_modify_with_timeout<F>(self, f: F) -> Self
487    where
488        F: FnOnce(&mut V),
489    {
490        self.and_modify(|v| f(v.value_mut()))
491    }
492}
493
494#[derive(Clone, Debug)]
495pub struct TimedValue<V>(V, Option<Instant>);
496
497impl<V> TimedValue<V> {
498    pub fn new(value: V, timeout_duration: Option<Duration>) -> Self {
499        TimedValue(value, timeout_duration.map(|t| Instant::now() + t))
500    }
501
502    pub fn value(&self) -> &V {
503        &self.0
504    }
505
506    pub fn value_mut(&mut self) -> &mut V {
507        &mut self.0
508    }
509
510    pub fn into_value(self) -> V {
511        self.0
512    }
513
514    pub fn is_expired(&self) -> bool {
515        self.1.map(|e| Instant::now() >= e).unwrap_or(false)
516    }
517}
518
519impl<V> PartialEq for TimedValue<V>
520where
521    V: PartialEq,
522{
523    fn eq(&self, other: &TimedValue<V>) -> bool {
524        self.value() == other.value()
525    }
526}
527
528//impl<V> Eq for TimedValue<V> where V: Eq {}
529
530#[test]
531fn test_cache_map_ext() {
532    use std::collections::hash_map::RandomState;
533
534    let mut m: HashMap<_, _, RandomState> = HashMap::default();
535    let old1 = m.insert("k1", TimedValue::new(1, None));
536    let old2 = m.insert("k2", TimedValue::new(2, Some(Duration::from_millis(50))));
537    let old3 = m.insert("k3", TimedValue::new(3, Some(Duration::from_millis(80))));
538    let old4 = m.insert("k4", TimedValue::new(4, Some(Duration::from_millis(130))));
539    let old44 = m.insert("k4", TimedValue::new(44, None));
540
541    let old6 = m.insert_with_timeout("k6", 6, Some(Duration::from_secs(150)));
542    let old7 = m.insert_with_timeout("k7", 7, None);
543
544    let old66 = m.insert_with_timeout("k6", 66, Some(Duration::from_secs(60)));
545
546    assert_eq!(old1, None);
547    assert_eq!(old2, None);
548    assert_eq!(old3, None);
549    assert_eq!(old4, None);
550    assert_eq!(old6, None);
551    assert_eq!(old7, None);
552
553    println!("old44: {:?}", old44);
554    assert_eq!(old44, Some(TimedValue::new(4, None)));
555
556    assert_eq!(old66, Some(6));
557    println!("old66: {:?}", old66);
558
559    let v6 = m.get("k6");
560    println!("v6: {:?}", v6);
561    assert_eq!(v6, Some(&TimedValue::new(66, None)));
562
563    m.get_with_timeout_mut("k6").map(|v| *v = 666);
564    let v6 = m.get_with_timeout("k6");
565    println!("v6: {:?}", v6);
566    assert_eq!(v6, Some(&666));
567
568    for i in 0..20 {
569        m.remove_expired_values();
570        println!(
571            "{} map len: {},  map k1: {:?}, k2: {:?}",
572            i,
573            m.len(),
574            m.get("k1"),
575            m.get_with_timeout("k2")
576        );
577        //        println!("{} map len: {},  map: {:?}", i, m.len(), m);
578        std::thread::sleep(std::time::Duration::from_millis(30));
579    }
580}
581
582#[test]
583fn test_btree_map_ext() {
584    let mut m: BTreeMap<_, _> = BTreeMap::default();
585
586    let old1 = m.insert_with_timeout("k1", 1, None);
587    let old2 = m.insert_with_timeout("k2", 2, Some(Duration::from_millis(50)));
588    let old3 = m.insert_with_timeout("k3", 3, Some(Duration::from_millis(80)));
589    let old4 = m.insert_with_timeout("k4", 4, Some(Duration::from_millis(130)));
590    let old44 = m.insert_with_timeout("k4", 44, None);
591
592    let old6 = m.insert_with_timeout("k6", 6, Some(Duration::from_secs(150)));
593    let old7 = m.insert_with_timeout("k7", 7, None);
594    let old66 = m.insert_with_timeout("k6", 66, Some(Duration::from_secs(60)));
595
596    assert_eq!(old1, None);
597    assert_eq!(old2, None);
598    assert_eq!(old3, None);
599    assert_eq!(old4, None);
600    assert_eq!(old6, None);
601    assert_eq!(old7, None);
602
603    println!("old44: {:?}", old44);
604    assert_eq!(old44, Some(4));
605    assert_eq!(m.get_with_timeout("k4"), Some(&44));
606
607    assert_eq!(old66, Some(6));
608    println!("old66: {:?}", old66);
609
610    let v6 = m.get("k6");
611    println!("v6: {:?}", v6);
612    assert_eq!(v6, Some(&TimedValue::new(66, None)));
613
614    m.get_with_timeout_mut("k6").map(|v| *v = 666);
615    let v6 = m.get_with_timeout("k6");
616    println!("v6: {:?}", v6);
617    assert_eq!(v6, Some(&666));
618
619    m.entry("kk1").or_insert_with_timeout_f(|| 10, None);
620    assert_eq!(m.get_with_timeout("kk1"), Some(&10));
621    m.entry("kk1").and_modify_with_timeout(|v| {
622        *v = 100;
623    });
624    assert_eq!(m.get_with_timeout("kk1"), Some(&100));
625    println!("kk1: {:?}", m.get_with_timeout("kk1"));
626}
627
628#[test]
629fn test_btree_map_ext_removes() {
630    let mut m: dequemap::DequeBTreeMap<_, _> = dequemap::DequeBTreeMap::default();
631    m.push_back(3, TimedValue::new((), Some(Duration::from_millis(800))));
632    std::thread::sleep(Duration::from_millis(100));
633    m.push_back(1, TimedValue::new((), Some(Duration::from_millis(800))));
634    std::thread::sleep(Duration::from_millis(100));
635    m.push_back(6, TimedValue::new((), Some(Duration::from_millis(800))));
636    std::thread::sleep(Duration::from_millis(100));
637    m.push_back(8, TimedValue::new((), Some(Duration::from_millis(800))));
638    std::thread::sleep(Duration::from_millis(100));
639    m.push_back(3, TimedValue::new((), Some(Duration::from_millis(800))));
640
641    assert_eq!(m.len(), 4);
642    for (key, item) in m.iter() {
643        println!("key: {:?}, is_expired: {}", key, item.is_expired());
644    }
645    println!("--------------------------------------------------------------");
646    std::thread::sleep(Duration::from_millis(600));
647
648    while let Some((key, item)) = m.front() {
649        println!("clean expired, key: {}", key);
650        if item.is_expired() {
651            m.pop_front();
652        } else {
653            break;
654        }
655    }
656    println!("m.len(): {}", m.len());
657    for (key, item) in m.iter() {
658        println!("key: {:?}, is_expired: {}", key, item.is_expired());
659    }
660    assert_eq!(m.len(), 2);
661}
662
663// ============== Additional tests ==============
664
665#[test]
666fn test_timed_value_new_no_expiry() {
667    let mut tv = TimedValue::new(42, None);
668    assert_eq!(*tv.value(), 42);
669    assert_eq!(*tv.value_mut(), 42);
670    assert_eq!(tv.into_value(), 42);
671}
672
673#[test]
674fn test_timed_value_with_duration() {
675    let tv = TimedValue::new(42, Some(std::time::Duration::from_secs(60)));
676    assert_eq!(*tv.value(), 42);
677    // Should not be expired immediately
678    assert!(!tv.is_expired());
679}
680
681#[test]
682fn test_timed_value_is_expired() {
683    // Use a very short duration and immediately check — unlikely to expire
684    let tv = TimedValue::new(42, Some(std::time::Duration::from_millis(1)));
685    // Sleep past the timeout
686    std::thread::sleep(std::time::Duration::from_millis(5));
687    assert!(tv.is_expired());
688}
689
690#[test]
691fn test_timed_value_value_mut_modify() {
692    let mut tv = TimedValue::new(1, None);
693    *tv.value_mut() = 100;
694    assert_eq!(*tv.value(), 100);
695}
696
697#[test]
698fn test_cache_map_ext_hashmap_basic() {
699    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
700
701    m.insert_with_timeout("a", 1, None);
702    m.insert_with_timeout("b", 2, Some(std::time::Duration::from_secs(60)));
703
704    assert_eq!(m.get_with_timeout("a"), Some(&1));
705    assert_eq!(m.get_with_timeout("b"), Some(&2));
706    assert_eq!(m.get_with_timeout("c"), None);
707}
708
709#[test]
710fn test_cache_map_ext_hashmap_overwrite() {
711    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
712
713    let old = m.insert_with_timeout("k", 1, None);
714    assert_eq!(old, None);
715
716    let old = m.insert_with_timeout("k", 2, None);
717    assert_eq!(old, Some(1));
718    assert_eq!(m.get_with_timeout("k"), Some(&2));
719}
720
721#[test]
722fn test_cache_map_ext_hashmap_expired_returns_none() {
723    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
724
725    m.insert(
726        "k",
727        TimedValue::new(1, Some(std::time::Duration::from_millis(1))),
728    );
729    std::thread::sleep(std::time::Duration::from_millis(5));
730    // Expired items are not returned
731    assert_eq!(m.get_with_timeout("k"), None);
732}
733
734#[test]
735fn test_cache_map_ext_hashmap_get_mut_and_set_ttl() {
736    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
737
738    m.insert_with_timeout("k", 1, None);
739    m.get_with_timeout_mut("k").map(|v| *v = 42);
740    assert_eq!(m.get_with_timeout("k"), Some(&42));
741}
742
743#[test]
744fn test_cache_map_ext_hashmap_remove_expired() {
745    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
746
747    m.insert(
748        "expired",
749        TimedValue::new(1, Some(std::time::Duration::from_millis(1))),
750    );
751    m.insert("keep", TimedValue::new(2, None));
752
753    std::thread::sleep(std::time::Duration::from_millis(5));
754    m.remove_expired_values();
755    assert_eq!(m.len(), 1);
756    assert!(m.contains_key("keep"));
757    assert!(!m.contains_key("expired"));
758}
759
760#[test]
761fn test_entry_ext_hashmap_or_insert_with_timeout() {
762    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
763
764    // Vacant entry
765    let val = m.entry("a").or_insert_with_timeout(10, None);
766    assert_eq!(*val, 10);
767
768    // Occupied entry (not expired)
769    let val = m.entry("a").or_insert_with_timeout(99, None);
770    assert_eq!(*val, 10);
771}
772
773#[test]
774fn test_entry_ext_hashmap_or_insert_with_timeout_expired() {
775    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
776
777    m.insert(
778        "a",
779        TimedValue::new(1, Some(std::time::Duration::from_millis(1))),
780    );
781    std::thread::sleep(std::time::Duration::from_millis(5));
782
783    // Expired entry — should be replaced
784    let val = m.entry("a").or_insert_with_timeout(200, None);
785    assert_eq!(*val, 200);
786}
787
788#[test]
789fn test_entry_ext_hashmap_or_insert_with_timeout_f() {
790    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
791
792    let val = m.entry("a").or_insert_with_timeout_f(|| 42, None);
793    assert_eq!(*val, 42);
794}
795
796#[test]
797fn test_entry_ext_hashmap_or_insert_with_timeout_key_f() {
798    let mut m: std::collections::HashMap<&str, TimedValue<String>> =
799        std::collections::HashMap::new();
800
801    let val = m
802        .entry("a")
803        .or_insert_with_timeout_key_f(|k| format!("val-{}", k), None);
804    assert_eq!(**val, "val-a".to_string());
805}
806
807#[test]
808fn test_entry_ext_hashmap_and_modify_with_timeout() {
809    let mut m: std::collections::HashMap<&str, TimedValue<i32>> = std::collections::HashMap::new();
810    m.insert("a", TimedValue::new(1, None));
811
812    m.entry("a").and_modify_with_timeout(|v| *v = 99);
813    assert_eq!(m.get_with_timeout("a"), Some(&99));
814}
815
816// ---------- BTreeMap ----------
817
818#[test]
819fn test_cache_map_ext_btreemap() {
820    let mut m: std::collections::BTreeMap<&str, TimedValue<i32>> =
821        std::collections::BTreeMap::new();
822
823    m.insert_with_timeout("a", 1, None);
824    m.insert_with_timeout("b", 2, Some(std::time::Duration::from_secs(60)));
825
826    assert_eq!(m.get_with_timeout("a"), Some(&1));
827    assert_eq!(m.get_with_timeout("b"), Some(&2));
828    assert_eq!(m.get_with_timeout("c"), None);
829
830    m.get_with_timeout_mut("a").map(|v| *v = 10);
831    assert_eq!(m.get_with_timeout("a"), Some(&10));
832}
833
834#[test]
835fn test_cache_map_ext_btreemap_remove_expired() {
836    let mut m: std::collections::BTreeMap<&str, TimedValue<i32>> =
837        std::collections::BTreeMap::new();
838
839    m.insert(
840        "expired",
841        TimedValue::new(1, Some(std::time::Duration::from_millis(1))),
842    );
843    m.insert("keep", TimedValue::new(2, None));
844
845    std::thread::sleep(std::time::Duration::from_millis(5));
846    m.remove_expired_values();
847    assert_eq!(m.len(), 1);
848    assert!(m.contains_key("keep"));
849}
850
851#[test]
852fn test_entry_ext_btreemap_or_insert_with_timeout() {
853    let mut m: std::collections::BTreeMap<&str, TimedValue<i32>> =
854        std::collections::BTreeMap::new();
855
856    let val = m.entry("a").or_insert_with_timeout(10, None);
857    assert_eq!(*val, 10);
858
859    let val = m.entry("a").or_insert_with_timeout(99, None);
860    assert_eq!(*val, 10);
861}
862
863#[test]
864fn test_entry_ext_btreemap_or_insert_with_timeout_f() {
865    let mut m: std::collections::BTreeMap<&str, TimedValue<i32>> =
866        std::collections::BTreeMap::new();
867
868    let val = m.entry("a").or_insert_with_timeout_f(|| 42, None);
869    assert_eq!(*val, 42);
870}
871
872#[test]
873fn test_entry_ext_btreemap_or_insert_with_timeout_key_f() {
874    let mut m: std::collections::BTreeMap<String, TimedValue<String>> =
875        std::collections::BTreeMap::new();
876
877    let val = m
878        .entry("key".to_string())
879        .or_insert_with_timeout_key_f(|k| k.clone(), None);
880    assert_eq!(**val, "key".to_string());
881}
882
883#[test]
884fn test_entry_ext_btreemap_and_modify_with_timeout() {
885    let mut m: std::collections::BTreeMap<&str, TimedValue<i32>> =
886        std::collections::BTreeMap::new();
887    m.insert("a", TimedValue::new(1, None));
888
889    m.entry("a").and_modify_with_timeout(|v| *v = 99);
890    assert_eq!(m.get_with_timeout("a"), Some(&99));
891}
892
893// ---------- DequeBTreeMap (via dequemap) ----------
894
895#[test]
896fn test_cache_map_ext_deque_btreemap() {
897    let mut m: dequemap::DequeBTreeMap<&str, TimedValue<i32>> = dequemap::DequeBTreeMap::default();
898
899    m.insert_with_timeout("a", 1, None);
900    m.insert_with_timeout("b", 2, Some(std::time::Duration::from_secs(60)));
901
902    assert_eq!(m.get_with_timeout("a"), Some(&1));
903    assert_eq!(m.get_with_timeout("b"), Some(&2));
904    assert_eq!(m.get_with_timeout("c"), None);
905
906    m.get_with_timeout_mut("a").map(|v| *v = 10);
907    assert_eq!(m.get_with_timeout("a"), Some(&10));
908}
909
910#[test]
911fn test_entry_ext_deque_btreemap_or_insert_with_timeout() {
912    let mut m: dequemap::DequeBTreeMap<&str, TimedValue<i32>> = dequemap::DequeBTreeMap::default();
913
914    let val = m.entry("a").or_insert_with_timeout(10, None);
915    assert_eq!(*val, 10);
916
917    let val = m.entry("a").or_insert_with_timeout(99, None);
918    assert_eq!(*val, 10);
919}
920
921#[test]
922fn test_entry_ext_deque_btreemap_and_modify_with_timeout() {
923    let mut m: dequemap::DequeBTreeMap<&str, TimedValue<i32>> = dequemap::DequeBTreeMap::default();
924    m.insert("a", TimedValue::new(1, None));
925
926    m.entry("a").and_modify_with_timeout(|v| *v = 99);
927    assert_eq!(m.get_with_timeout("a"), Some(&99));
928}
929
930// ---------- DequeHashMap (via dequemap) ----------
931
932#[test]
933fn test_cache_map_ext_deque_hashmap() {
934    let mut m: dequemap::DequeHashMap<&str, TimedValue<i32>> = dequemap::DequeHashMap::default();
935
936    m.insert_with_timeout("a", 1, None);
937    m.insert_with_timeout("b", 2, Some(std::time::Duration::from_secs(60)));
938
939    assert_eq!(m.get_with_timeout("a"), Some(&1));
940    assert_eq!(m.get_with_timeout("b"), Some(&2));
941    assert_eq!(m.get_with_timeout("c"), None);
942}
943
944#[test]
945fn test_entry_ext_deque_hashmap_or_insert_with_timeout() {
946    let mut m: dequemap::DequeHashMap<&str, TimedValue<i32>> = dequemap::DequeHashMap::default();
947
948    let val = m.entry("a").or_insert_with_timeout(10, None);
949    assert_eq!(*val, 10);
950
951    let val = m.entry("a").or_insert_with_timeout(99, None);
952    assert_eq!(*val, 10);
953}
954
955#[test]
956fn test_entry_ext_deque_hashmap_and_modify() {
957    let mut m: dequemap::DequeHashMap<&str, TimedValue<i32>> = dequemap::DequeHashMap::default();
958    m.insert("a", TimedValue::new(1, None));
959
960    m.entry("a").and_modify_with_timeout(|v| *v = 99);
961    assert_eq!(m.get_with_timeout("a"), Some(&99));
962}
963
964#[test]
965fn test_timed_value_partial_eq() {
966    let tv1 = TimedValue::new(42, None);
967    let tv2 = TimedValue::new(42, Some(std::time::Duration::from_secs(60)));
968    let tv3 = TimedValue::new(99, None);
969
970    assert_eq!(tv1, tv2);
971    assert_ne!(tv1, tv3);
972}