1use super::*;
2
3macro_rules! cfg_std_feature {
4 ($($item:item)*) => {
5 $(
6 #[cfg(feature = "std")]
7 $item
8 )*
9 };
10}
11
12macro_rules! cfg_not_std_feature {
13 ($($item:item)*) => {
14 $(
15 #[cfg(not(feature = "std"))]
16 $item
17 )*
18 };
19}
20
21cfg_not_std_feature! {
22 pub trait GenericKey: Clone + Eq + Ord {}
25 impl<T: Clone + Eq + Ord> GenericKey for T {}
26}
27
28cfg_std_feature! {
29 pub trait GenericKey: Clone + Eq + Ord + Hash {}
32 impl<T: Clone + Eq + Ord + Hash> GenericKey for T {}
33}
34
35#[allow(clippy::enum_variant_names)]
37#[derive(Debug)]
38enum GenericMap<K, V> {
39 BTreeMap(BTreeMap<K, V>),
40 #[cfg(feature = "std")]
41 HashMap(HashMap<K, V>),
42 #[cfg(all(feature = "std", feature = "rustc-hash"))]
43 FxHashMap(FxHashMap<K, V>),
44}
45
46impl<K, V> Default for GenericMap<K, V> {
47 fn default() -> Self {
48 Self::BTreeMap(BTreeMap::default())
49 }
50}
51
52impl<K, V> GenericMap<K, V>
53where
54 K: GenericKey,
55{
56 #[inline(always)]
57 fn get(&self, k: &K) -> Option<&V> {
58 match self {
59 Self::BTreeMap(inner) => inner.get(k),
60 #[cfg(feature = "std")]
61 Self::HashMap(inner) => inner.get(k),
62 #[cfg(all(feature = "std", feature = "rustc-hash"))]
63 Self::FxHashMap(inner) => inner.get(k),
64 }
65 }
66
67 #[inline(always)]
68 fn get_mut(&mut self, k: &K) -> Option<&mut V> {
69 match self {
70 Self::BTreeMap(inner) => inner.get_mut(k),
71 #[cfg(feature = "std")]
72 Self::HashMap(inner) => inner.get_mut(k),
73 #[cfg(all(feature = "std", feature = "rustc-hash"))]
74 Self::FxHashMap(inner) => inner.get_mut(k),
75 }
76 }
77
78 #[inline(always)]
79 fn len(&self) -> usize {
80 match self {
81 Self::BTreeMap(inner) => inner.len(),
82 #[cfg(feature = "std")]
83 Self::HashMap(inner) => inner.len(),
84 #[cfg(all(feature = "std", feature = "rustc-hash"))]
85 Self::FxHashMap(inner) => inner.len(),
86 }
87 }
88
89 #[inline(always)]
90 fn keys(&self) -> Vec<K> {
91 match self {
92 Self::BTreeMap(inner) => inner.keys().cloned().collect(),
93 #[cfg(feature = "std")]
94 Self::HashMap(inner) => inner.keys().cloned().collect(),
95 #[cfg(all(feature = "std", feature = "rustc-hash"))]
96 Self::FxHashMap(inner) => inner.keys().cloned().collect(),
97 }
98 }
99
100 #[inline(always)]
101 fn is_empty(&self) -> bool {
102 match self {
103 Self::BTreeMap(inner) => inner.is_empty(),
104 #[cfg(feature = "std")]
105 Self::HashMap(inner) => inner.is_empty(),
106 #[cfg(all(feature = "std", feature = "rustc-hash"))]
107 Self::FxHashMap(inner) => inner.is_empty(),
108 }
109 }
110
111 #[inline(always)]
112 fn insert(&mut self, k: K, v: V) -> Option<V> {
113 match self {
114 Self::BTreeMap(inner) => inner.insert(k, v),
115 #[cfg(feature = "std")]
116 Self::HashMap(inner) => inner.insert(k, v),
117 #[cfg(all(feature = "std", feature = "rustc-hash"))]
118 Self::FxHashMap(inner) => inner.insert(k, v),
119 }
120 }
121
122 #[inline(always)]
123 fn clear(&mut self) {
124 match self {
125 Self::BTreeMap(inner) => inner.clear(),
126 #[cfg(feature = "std")]
127 Self::HashMap(inner) => inner.clear(),
128 #[cfg(all(feature = "std", feature = "rustc-hash"))]
129 Self::FxHashMap(inner) => inner.clear(),
130 }
131 }
132
133 #[inline(always)]
134 fn remove(&mut self, k: &K) -> Option<V> {
135 match self {
136 Self::BTreeMap(inner) => inner.remove(k),
137 #[cfg(feature = "std")]
138 Self::HashMap(inner) => inner.remove(k),
139 #[cfg(all(feature = "std", feature = "rustc-hash"))]
140 Self::FxHashMap(inner) => inner.remove(k),
141 }
142 }
143}
144
145#[cfg(feature = "std")]
147#[allow(clippy::enum_variant_names)]
148pub enum MapKind {
149 BTreeMap,
150 HashMap,
151 #[cfg(feature = "rustc-hash")]
152 FxHashMap,
153}
154
155#[derive(Debug)]
162pub struct TimedMap<K, V, #[cfg(feature = "std")] C = StdClock, #[cfg(not(feature = "std"))] C> {
163 clock: C,
164
165 map: GenericMap<K, ExpirableEntry<V>>,
166 expiries: BTreeMap<u64, BTreeSet<K>>,
167
168 expiration_tick: u16,
169 expiration_tick_cap: u16,
170}
171
172#[cfg(feature = "serde")]
173impl<K: serde::Serialize + Ord, V: serde::Serialize, C: Clock> serde::Serialize
174 for TimedMap<K, V, C>
175{
176 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177 where
178 S: serde::Serializer,
179 {
180 let now = self.clock.elapsed_seconds_since_creation();
181 match &self.map {
182 GenericMap::BTreeMap(inner) => {
183 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
184 serializer.collect_map(map)
185 }
186 #[cfg(feature = "std")]
187 GenericMap::HashMap(inner) => {
188 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
189 serializer.collect_map(map)
190 }
191 #[cfg(all(feature = "std", feature = "rustc-hash"))]
192 GenericMap::FxHashMap(inner) => {
193 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
194 serializer.collect_map(map)
195 }
196 }
197 }
198}
199
200impl<K, V, C> Default for TimedMap<K, V, C>
201where
202 C: Default,
203{
204 fn default() -> Self {
205 Self {
206 clock: Default::default(),
207
208 map: GenericMap::default(),
209 expiries: BTreeMap::default(),
210
211 expiration_tick: 0,
212 expiration_tick_cap: 1,
213 }
214 }
215}
216
217#[cfg(feature = "std")]
218impl<K, V> TimedMap<K, V, StdClock>
219where
220 K: GenericKey,
221{
222 pub fn new() -> Self {
224 Self::default()
225 }
226
227 pub fn new_with_map_kind(map_kind: MapKind) -> Self {
229 let map = match map_kind {
230 MapKind::BTreeMap => GenericMap::<K, ExpirableEntry<V>>::BTreeMap(BTreeMap::default()),
231 MapKind::HashMap => GenericMap::HashMap(HashMap::default()),
232 #[cfg(feature = "rustc-hash")]
233 MapKind::FxHashMap => GenericMap::FxHashMap(FxHashMap::default()),
234 };
235
236 Self {
237 map,
238
239 clock: StdClock::default(),
240 expiries: BTreeMap::default(),
241
242 expiration_tick: 0,
243 expiration_tick_cap: 1,
244 }
245 }
246}
247
248impl<K, V, C> TimedMap<K, V, C>
249where
250 C: Clock,
251 K: GenericKey,
252{
253 #[cfg(not(feature = "std"))]
257 pub fn new(clock: C) -> Self {
258 Self {
259 clock,
260 map: GenericMap::default(),
261 expiries: BTreeMap::default(),
262 expiration_tick: 0,
263 expiration_tick_cap: 1,
264 }
265 }
266
267 #[inline(always)]
278 pub fn expiration_tick_cap(mut self, expiration_tick_cap: u16) -> Self {
279 self.expiration_tick_cap = expiration_tick_cap;
280 self
281 }
282
283 pub fn get(&self, k: &K) -> Option<&V> {
287 self.map
288 .get(k)
289 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
290 .map(|v| v.value())
291 }
292
293 pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
297 self.map
298 .get_mut(k)
299 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
300 .map(|v| v.value_mut())
301 }
302
303 #[inline(always)]
307 pub fn get_unchecked(&self, k: &K) -> Option<&V> {
308 self.map.get(k).map(|v| v.value())
309 }
310
311 #[inline(always)]
316 pub fn get_mut_unchecked(&mut self, k: &K) -> Option<&mut V> {
317 self.map.get_mut(k).map(|v| v.value_mut())
318 }
319
320 pub fn get_remaining_duration(&self, k: &K) -> Option<Duration> {
324 match self.map.get(k) {
325 Some(v) => {
326 let now = self.clock.elapsed_seconds_since_creation();
327 if v.is_expired(now) {
328 return None;
329 }
330
331 v.remaining_duration(now)
332 }
333 None => None,
334 }
335 }
336
337 #[inline(always)]
341 pub fn len(&self) -> usize {
342 self.map.len() - self.len_expired()
343 }
344
345 #[inline(always)]
349 pub fn len_expired(&self) -> usize {
350 let now = self.clock.elapsed_seconds_since_creation();
351 self.expiries
352 .iter()
353 .filter_map(
354 |(exp, keys)| {
355 if exp <= &now {
356 Some(keys.len())
357 } else {
358 None
359 }
360 },
361 )
362 .sum()
363 }
364
365 #[inline(always)]
369 pub fn len_unchecked(&self) -> usize {
370 self.map.len()
371 }
372
373 #[inline(always)]
375 pub fn keys(&self) -> Vec<K> {
376 self.map.keys()
377 }
378
379 #[inline(always)]
381 pub fn is_empty(&self) -> bool {
382 self.map.is_empty()
383 }
384
385 #[inline(always)]
391 fn insert(&mut self, k: K, v: V, expires_at: Option<u64>) -> Option<V> {
392 let entry = ExpirableEntry::new(v, expires_at);
393 match self.map.insert(k.clone(), entry) {
394 Some(old) => {
395 if let EntryStatus::ExpiresAtSeconds(e) = old.status() {
397 self.drop_key_from_expiry(e, &k)
398 }
399
400 Some(old.owned_value())
401 }
402 None => None,
403 }
404 }
405
406 pub fn insert_expirable(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
415 self.expiration_tick += 1;
416
417 let now = self.clock.elapsed_seconds_since_creation();
418 if self.expiration_tick >= self.expiration_tick_cap {
419 self.drop_expired_entries_inner(now);
420 self.expiration_tick = 0;
421 }
422
423 let expires_at = now + duration.as_secs();
424
425 let res = self.insert(k.clone(), v, Some(expires_at));
426
427 self.expiries.entry(expires_at).or_default().insert(k);
428
429 res
430 }
431
432 pub fn insert_expirable_unchecked(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
441 let now = self.clock.elapsed_seconds_since_creation();
442 let expires_at = now + duration.as_secs();
443
444 let res = self.insert(k.clone(), v, Some(expires_at));
445
446 self.expiries.entry(expires_at).or_default().insert(k);
447
448 res
449 }
450
451 pub fn insert_constant(&mut self, k: K, v: V) -> Option<V> {
460 self.expiration_tick += 1;
461
462 let now = self.clock.elapsed_seconds_since_creation();
463 if self.expiration_tick >= self.expiration_tick_cap {
464 self.drop_expired_entries_inner(now);
465 self.expiration_tick = 0;
466 }
467
468 self.insert(k, v, None)
469 }
470
471 pub fn insert_constant_unchecked(&mut self, k: K, v: V) -> Option<V> {
480 self.expiration_tick += 1;
481 self.insert(k, v, None)
482 }
483
484 #[inline(always)]
490 pub fn remove(&mut self, k: &K) -> Option<V> {
491 self.map
492 .remove(k)
493 .filter(|v| {
494 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
495 self.drop_key_from_expiry(expires_at_seconds, k);
496 }
497
498 !v.is_expired(self.clock.elapsed_seconds_since_creation())
499 })
500 .map(|v| v.owned_value())
501 }
502
503 #[inline(always)]
508 pub fn remove_unchecked(&mut self, k: &K) -> Option<V> {
509 self.map
510 .remove(k)
511 .filter(|v| {
512 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
513 self.drop_key_from_expiry(expires_at_seconds, k);
514 }
515
516 true
517 })
518 .map(|v| v.owned_value())
519 }
520
521 #[inline(always)]
523 pub fn clear(&mut self) {
524 self.map.clear()
525 }
526
527 pub fn update_expiration_status(
532 &mut self,
533 key: K,
534 duration: Duration,
535 ) -> Result<Option<EntryStatus>, &'static str> {
536 match self.map.get_mut(&key) {
537 Some(entry) => {
538 let old_status = *entry.status();
539 let now = self.clock.elapsed_seconds_since_creation();
540 let expires_at = now + duration.as_secs();
541
542 entry.update_status(EntryStatus::ExpiresAtSeconds(expires_at));
543
544 if let EntryStatus::ExpiresAtSeconds(t) = &old_status {
545 self.drop_key_from_expiry(t, &key);
546 }
547 self.expiries
548 .entry(expires_at)
549 .or_default()
550 .insert(key.clone());
551
552 Ok(Some(old_status))
553 }
554 None => Err("entry not found"),
555 }
556 }
557
558 #[inline(always)]
563 pub fn drop_expired_entries(&mut self) {
564 let now = self.clock.elapsed_seconds_since_creation();
565 self.drop_expired_entries_inner(now);
566 }
567
568 fn drop_expired_entries_inner(&mut self, now: u64) {
569 while let Some((exp, keys)) = self.expiries.pop_first() {
571 if exp > now {
573 self.expiries.insert(exp, keys);
574 break;
575 }
576
577 for key in keys {
578 self.map.remove(&key);
579 }
580 }
581 }
582
583 fn drop_key_from_expiry(&mut self, expiry_key: &u64, map_key: &K) {
584 if let Some(list) = self.expiries.get_mut(expiry_key) {
585 list.remove(map_key);
586
587 if list.is_empty() {
588 self.expiries.remove(expiry_key);
589 }
590 }
591 }
592
593 #[inline(always)]
597 pub fn contains_key(&self, k: &K) -> bool {
598 self.get(k).is_some()
599 }
600
601 #[inline(always)]
605 pub fn contains_key_unchecked(&self, k: &K) -> bool {
606 self.map.keys().contains(k)
607 }
608}
609
610#[cfg(test)]
611#[cfg(not(feature = "std"))]
612mod tests {
613 use super::*;
614
615 struct MockClock {
616 current_time: u64,
617 }
618
619 impl Clock for MockClock {
620 fn elapsed_seconds_since_creation(&self) -> u64 {
621 self.current_time
622 }
623 }
624
625 #[test]
626 fn nostd_insert_and_get_constant_entry() {
627 let clock = MockClock { current_time: 1000 };
628 let mut map = TimedMap::new(clock);
629
630 map.insert_constant(1, "constant value");
631
632 assert_eq!(map.get(&1), Some(&"constant value"));
633 assert_eq!(map.get_remaining_duration(&1), None);
634 }
635
636 #[test]
637 fn nostd_insert_and_get_expirable_entry() {
638 let clock = MockClock { current_time: 1000 };
639 let mut map = TimedMap::new(clock);
640 let duration = Duration::from_secs(60);
641
642 map.insert_expirable(1, "expirable value", duration);
643
644 assert_eq!(map.get(&1), Some(&"expirable value"));
645 assert_eq!(map.get_remaining_duration(&1), Some(duration));
646 }
647
648 #[test]
649 fn nostd_expired_entry() {
650 let clock = MockClock { current_time: 1000 };
651 let mut map = TimedMap::new(clock);
652 let duration = Duration::from_secs(60);
653
654 map.insert_expirable(1, "expirable value", duration);
656
657 let clock = MockClock { current_time: 1070 };
659 map.clock = clock;
660
661 assert_eq!(map.get(&1), None);
663 assert_eq!(map.get_remaining_duration(&1), None);
664 }
665
666 #[test]
667 fn nostd_remove_entry() {
668 let clock = MockClock { current_time: 1000 };
669 let mut map = TimedMap::new(clock);
670
671 map.insert_constant(1, "constant value");
672
673 assert_eq!(map.remove(&1), Some("constant value"));
674 assert_eq!(map.get(&1), None);
675 }
676
677 #[test]
678 fn nostd_drop_expired_entries() {
679 let clock = MockClock { current_time: 1000 };
680 let mut map = TimedMap::new(clock);
681
682 map.insert_expirable(1, "expirable value1", Duration::from_secs(50));
684 map.insert_expirable(2, "expirable value2", Duration::from_secs(70));
685 map.insert_constant(3, "constant value");
686
687 let clock = MockClock { current_time: 1055 };
689 map.clock = clock;
690
691 assert_eq!(map.get(&1), None);
693 assert_eq!(map.get(&2), Some(&"expirable value2"));
694 assert_eq!(map.get(&3), Some(&"constant value"));
695
696 let clock = MockClock { current_time: 1071 };
698 map.clock = clock;
699
700 assert_eq!(map.get(&1), None);
701 assert_eq!(map.get(&2), None);
702 assert_eq!(map.get(&3), Some(&"constant value"));
703 }
704
705 #[test]
706 fn nostd_update_existing_entry() {
707 let clock = MockClock { current_time: 1000 };
708 let mut map = TimedMap::new(clock);
709
710 map.insert_constant(1, "initial value");
711 assert_eq!(map.get(&1), Some(&"initial value"));
712
713 map.insert_expirable(1, "updated value", Duration::from_secs(15));
715 assert_eq!(map.get(&1), Some(&"updated value"));
716
717 let clock = MockClock { current_time: 1016 };
719 map.clock = clock;
720
721 assert_eq!(map.get(&1), None);
722 }
723
724 #[test]
725 fn nostd_update_expirable_entry_status() {
726 let clock = MockClock { current_time: 1000 };
727 let mut map = TimedMap::new(clock);
728
729 map.insert_constant(1, "initial value");
730 assert_eq!(map.get(&1), Some(&"initial value"));
731
732 map.update_expiration_status(1, Duration::from_secs(16))
734 .expect("entry update shouldn't fail");
735 assert_eq!(map.get(&1), Some(&"initial value"));
736
737 let clock = MockClock { current_time: 1017 };
739 map.clock = clock;
740 assert_eq!(map.get(&1), None);
741 }
742
743 #[test]
744 fn nostd_update_expirable_entry_status_with_previou_time() {
745 let clock = MockClock { current_time: 1000 };
746 let mut map = TimedMap::new(clock);
747
748 map.insert_expirable(1, "expirable value", Duration::from_secs(15));
750 map.update_expiration_status(1, Duration::from_secs(15))
751 .expect("entry update shouldn't fail");
752
753 assert_eq!(map.get(&1), Some(&"expirable value"));
755 assert!(map.expiries.contains_key(&1015));
756 }
757
758 #[test]
759 fn nostd_contains_key() {
760 let clock = MockClock { current_time: 1000 };
761 let mut map = TimedMap::new(clock);
762
763 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
765 assert!(map.contains_key(&1));
766 }
767}
768
769#[cfg(feature = "std")]
770#[cfg(test)]
771mod std_tests {
772 use core::ops::Add;
773
774 use super::*;
775
776 #[test]
777 fn std_expirable_and_constant_entries() {
778 let mut map = TimedMap::new();
779
780 map.insert_constant(1, "constant value");
781 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
782
783 assert_eq!(map.get(&1), Some(&"constant value"));
784 assert_eq!(map.get(&2), Some(&"expirable value"));
785
786 assert_eq!(map.get_remaining_duration(&1), None);
787 assert!(map.get_remaining_duration(&2).is_some());
788 }
789
790 #[test]
791 fn std_expired_entry_removal() {
792 let mut map = TimedMap::new();
793 let duration = Duration::from_secs(2);
794
795 map.insert_expirable(1, "expirable value", duration);
796
797 std::thread::sleep(Duration::from_secs(3));
799
800 assert_eq!(map.get(&1), None);
802 assert_eq!(map.get_remaining_duration(&1), None);
803 }
804
805 #[test]
806 fn std_remove_entry() {
807 let mut map = TimedMap::new();
808
809 map.insert_constant(1, "constant value");
810 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
811
812 assert_eq!(map.remove(&1), Some("constant value"));
813 assert_eq!(map.remove(&2), Some("expirable value"));
814
815 assert_eq!(map.get(&1), None);
816 assert_eq!(map.get(&2), None);
817 }
818
819 #[test]
820 fn std_drop_expired_entries() {
821 let mut map = TimedMap::new();
822
823 map.insert_expirable(1, "expirable value1", Duration::from_secs(2));
824 map.insert_expirable(2, "expirable value2", Duration::from_secs(4));
825
826 std::thread::sleep(Duration::from_secs(3));
828
829 assert_eq!(map.get(&1), None);
831 assert_eq!(map.get(&2), Some(&"expirable value2"));
832 }
833
834 #[test]
835 fn std_update_existing_entry() {
836 let mut map = TimedMap::new();
837
838 map.insert_constant(1, "initial value");
839 assert_eq!(map.get(&1), Some(&"initial value"));
840
841 map.insert_expirable(1, "updated value", Duration::from_secs(1));
843 assert_eq!(map.get(&1), Some(&"updated value"));
844
845 std::thread::sleep(Duration::from_secs(2));
846
847 assert_eq!(map.get(&1), None);
849 }
850
851 #[test]
852 fn std_insert_constant_and_expirable_combined() {
853 let mut map = TimedMap::new();
854
855 map.insert_constant(1, "constant value");
857 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
858
859 assert_eq!(map.get(&1), Some(&"constant value"));
861 assert_eq!(map.get(&2), Some(&"expirable value"));
862
863 std::thread::sleep(Duration::from_secs(3));
865
866 assert_eq!(map.get(&1), Some(&"constant value"));
868 assert_eq!(map.get(&2), None);
869 }
870
871 #[test]
872 fn std_expirable_entry_still_valid_before_expiration() {
873 let mut map = TimedMap::new();
874
875 map.insert_expirable(1, "expirable value", Duration::from_secs(3));
877
878 std::thread::sleep(Duration::from_secs(2));
880
881 assert_eq!(map.get(&1), Some(&"expirable value"));
883 assert!(map.get_remaining_duration(&1).unwrap().as_secs() == 1);
884 }
885
886 #[test]
887 fn std_length_functions() {
888 let mut map = TimedMap::new();
889
890 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
891 map.insert_expirable(2, "expirable value", Duration::from_secs(1));
892 map.insert_expirable(3, "expirable value", Duration::from_secs(3));
893 map.insert_expirable(4, "expirable value", Duration::from_secs(3));
894 map.insert_expirable(5, "expirable value", Duration::from_secs(3));
895 map.insert_expirable(6, "expirable value", Duration::from_secs(3));
896
897 std::thread::sleep(Duration::from_secs(2).add(Duration::from_millis(1)));
898
899 assert_eq!(map.len(), 4);
900 assert_eq!(map.len_expired(), 2);
901 assert_eq!(map.len_unchecked(), 6);
902 }
903
904 #[test]
905 fn std_update_expirable_entry() {
906 let mut map = TimedMap::new();
907
908 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
909 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
910
911 std::thread::sleep(Duration::from_secs(2));
912
913 assert!(!map.expiries.contains_key(&1));
914 assert!(map.expiries.contains_key(&5));
915 assert_eq!(map.get(&1), Some(&"expirable value"));
916 }
917
918 #[test]
919 fn std_update_expirable_entry_status() {
920 let mut map = TimedMap::new();
921
922 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
923 map.update_expiration_status(1, Duration::from_secs(5))
924 .expect("entry update shouldn't fail");
925
926 std::thread::sleep(Duration::from_secs(3));
927 assert!(!map.expiries.contains_key(&1));
928 assert!(map.expiries.contains_key(&5));
929 assert_eq!(map.get(&1), Some(&"expirable value"));
930 }
931
932 #[test]
933 fn std_update_expirable_entry_status_with_previou_time() {
934 let mut map = TimedMap::new();
935
936 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
938 map.update_expiration_status(1, Duration::from_secs(5))
939 .expect("entry update shouldn't fail");
940
941 assert_eq!(map.get(&1), Some(&"expirable value"));
943 assert!(map.expiries.contains_key(&5));
944 }
945
946 #[test]
947 fn std_contains_key() {
948 let mut map = TimedMap::new();
949
950 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
952 assert!(map.contains_key(&1));
953 }
954
955 #[test]
956 fn std_does_not_contain_key_anymore() {
957 let mut map = TimedMap::new();
958
959 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
961 std::thread::sleep(Duration::from_secs(2));
962 assert!(!map.contains_key(&1));
963 }
964
965 #[test]
966 fn std_contains_key_unchecked() {
967 let mut map = TimedMap::new();
968
969 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
970 std::thread::sleep(Duration::from_secs(2));
971 assert!(map.contains_key_unchecked(&1));
972 }
973
974 #[test]
975 #[cfg(feature = "serde")]
976 fn ignore_expired_entries_on_serialize() {
977 let mut map = TimedMap::new();
978
979 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
980 map.insert_expirable(2, "expirable value2", Duration::from_secs(5));
981 std::thread::sleep(Duration::from_secs(2));
982
983 let actual = serde_json::json!(map);
984 let expected = serde_json::json!({
985 "2": "expirable value2"
986 });
987
988 assert_eq!(expected, actual);
989 }
990}