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> serde::Serialize for TimedMap<K, V, C> {
174 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175 where
176 S: serde::Serializer,
177 {
178 match &self.map {
179 GenericMap::BTreeMap(inner) => serializer.collect_map(inner),
180 #[cfg(feature = "std")]
181 GenericMap::HashMap(inner) => serializer.collect_map(inner),
182 #[cfg(all(feature = "std", feature = "rustc-hash"))]
183 GenericMap::FxHashMap(inner) => serializer.collect_map(inner),
184 }
185 }
186}
187
188impl<K, V, C> Default for TimedMap<K, V, C>
189where
190 C: Default,
191{
192 fn default() -> Self {
193 Self {
194 clock: Default::default(),
195
196 map: GenericMap::default(),
197 expiries: BTreeMap::default(),
198
199 expiration_tick: 0,
200 expiration_tick_cap: 1,
201 }
202 }
203}
204
205#[cfg(feature = "std")]
206impl<K, V> TimedMap<K, V, StdClock>
207where
208 K: GenericKey,
209{
210 pub fn new() -> Self {
212 Self::default()
213 }
214
215 pub fn new_with_map_kind(map_kind: MapKind) -> Self {
217 let map = match map_kind {
218 MapKind::BTreeMap => GenericMap::<K, ExpirableEntry<V>>::BTreeMap(BTreeMap::default()),
219 MapKind::HashMap => GenericMap::HashMap(HashMap::default()),
220 #[cfg(feature = "rustc-hash")]
221 MapKind::FxHashMap => GenericMap::FxHashMap(FxHashMap::default()),
222 };
223
224 Self {
225 map,
226
227 clock: StdClock::default(),
228 expiries: BTreeMap::default(),
229
230 expiration_tick: 0,
231 expiration_tick_cap: 1,
232 }
233 }
234}
235
236impl<K, V, C> TimedMap<K, V, C>
237where
238 C: Clock,
239 K: GenericKey,
240{
241 #[cfg(not(feature = "std"))]
245 pub fn new(clock: C) -> Self {
246 Self {
247 clock,
248 map: GenericMap::default(),
249 expiries: BTreeMap::default(),
250 expiration_tick: 0,
251 expiration_tick_cap: 1,
252 }
253 }
254
255 #[inline(always)]
266 pub fn expiration_tick_cap(mut self, expiration_tick_cap: u16) -> Self {
267 self.expiration_tick_cap = expiration_tick_cap;
268 self
269 }
270
271 pub fn get(&self, k: &K) -> Option<&V> {
275 self.map
276 .get(k)
277 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
278 .map(|v| v.value())
279 }
280
281 pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
285 self.map
286 .get_mut(k)
287 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
288 .map(|v| v.value_mut())
289 }
290
291 #[inline(always)]
295 pub fn get_unchecked(&self, k: &K) -> Option<&V> {
296 self.map.get(k).map(|v| v.value())
297 }
298
299 #[inline(always)]
304 pub fn get_mut_unchecked(&mut self, k: &K) -> Option<&mut V> {
305 self.map.get_mut(k).map(|v| v.value_mut())
306 }
307
308 pub fn get_remaining_duration(&self, k: &K) -> Option<Duration> {
312 match self.map.get(k) {
313 Some(v) => {
314 let now = self.clock.elapsed_seconds_since_creation();
315 if v.is_expired(now) {
316 return None;
317 }
318
319 v.remaining_duration(now)
320 }
321 None => None,
322 }
323 }
324
325 #[inline(always)]
329 pub fn len(&self) -> usize {
330 self.map.len() - self.len_expired()
331 }
332
333 #[inline(always)]
337 pub fn len_expired(&self) -> usize {
338 let now = self.clock.elapsed_seconds_since_creation();
339 self.expiries
340 .iter()
341 .filter_map(
342 |(exp, keys)| {
343 if exp <= &now {
344 Some(keys.len())
345 } else {
346 None
347 }
348 },
349 )
350 .sum()
351 }
352
353 #[inline(always)]
357 pub fn len_unchecked(&self) -> usize {
358 self.map.len()
359 }
360
361 #[inline(always)]
363 pub fn keys(&self) -> Vec<K> {
364 self.map.keys()
365 }
366
367 #[inline(always)]
369 pub fn is_empty(&self) -> bool {
370 self.map.is_empty()
371 }
372
373 #[inline(always)]
379 fn insert(&mut self, k: K, v: V, expires_at: Option<u64>) -> Option<V> {
380 let entry = ExpirableEntry::new(v, expires_at);
381 match self.map.insert(k.clone(), entry) {
382 Some(old) => {
383 if let EntryStatus::ExpiresAtSeconds(e) = old.status() {
385 self.drop_key_from_expiry(e, &k)
386 }
387
388 Some(old.owned_value())
389 }
390 None => None,
391 }
392 }
393
394 pub fn insert_expirable(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
403 self.expiration_tick += 1;
404
405 let now = self.clock.elapsed_seconds_since_creation();
406 if self.expiration_tick >= self.expiration_tick_cap {
407 self.drop_expired_entries_inner(now);
408 self.expiration_tick = 0;
409 }
410
411 let expires_at = now + duration.as_secs();
412
413 let res = self.insert(k.clone(), v, Some(expires_at));
414
415 self.expiries.entry(expires_at).or_default().insert(k);
416
417 res
418 }
419
420 pub fn insert_expirable_unchecked(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
429 let now = self.clock.elapsed_seconds_since_creation();
430 let expires_at = now + duration.as_secs();
431
432 let res = self.insert(k.clone(), v, Some(expires_at));
433
434 self.expiries.entry(expires_at).or_default().insert(k);
435
436 res
437 }
438
439 pub fn insert_constant(&mut self, k: K, v: V) -> Option<V> {
448 self.expiration_tick += 1;
449
450 let now = self.clock.elapsed_seconds_since_creation();
451 if self.expiration_tick >= self.expiration_tick_cap {
452 self.drop_expired_entries_inner(now);
453 self.expiration_tick = 0;
454 }
455
456 self.insert(k, v, None)
457 }
458
459 pub fn insert_constant_unchecked(&mut self, k: K, v: V) -> Option<V> {
468 self.expiration_tick += 1;
469 self.insert(k, v, None)
470 }
471
472 #[inline(always)]
478 pub fn remove(&mut self, k: &K) -> Option<V> {
479 self.map
480 .remove(k)
481 .filter(|v| {
482 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
483 self.drop_key_from_expiry(expires_at_seconds, k);
484 }
485
486 !v.is_expired(self.clock.elapsed_seconds_since_creation())
487 })
488 .map(|v| v.owned_value())
489 }
490
491 #[inline(always)]
496 pub fn remove_unchecked(&mut self, k: &K) -> Option<V> {
497 self.map
498 .remove(k)
499 .filter(|v| {
500 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
501 self.drop_key_from_expiry(expires_at_seconds, k);
502 }
503
504 true
505 })
506 .map(|v| v.owned_value())
507 }
508
509 #[inline(always)]
511 pub fn clear(&mut self) {
512 self.map.clear()
513 }
514
515 pub fn update_expiration_status(
520 &mut self,
521 key: K,
522 duration: Duration,
523 ) -> Result<Option<EntryStatus>, &'static str> {
524 match self.map.get_mut(&key) {
525 Some(entry) => {
526 let old_status = *entry.status();
527 let now = self.clock.elapsed_seconds_since_creation();
528 let expires_at = now + duration.as_secs();
529
530 entry.update_status(EntryStatus::ExpiresAtSeconds(expires_at));
531
532 if let EntryStatus::ExpiresAtSeconds(t) = &old_status {
533 self.drop_key_from_expiry(t, &key);
534 }
535 self.expiries
536 .entry(expires_at)
537 .or_default()
538 .insert(key.clone());
539
540 Ok(Some(old_status))
541 }
542 None => Err("entry not found"),
543 }
544 }
545
546 #[inline(always)]
551 pub fn drop_expired_entries(&mut self) {
552 let now = self.clock.elapsed_seconds_since_creation();
553 self.drop_expired_entries_inner(now);
554 }
555
556 fn drop_expired_entries_inner(&mut self, now: u64) {
557 while let Some((exp, keys)) = self.expiries.pop_first() {
559 if exp > now {
561 self.expiries.insert(exp, keys);
562 break;
563 }
564
565 for key in keys {
566 self.map.remove(&key);
567 }
568 }
569 }
570
571 fn drop_key_from_expiry(&mut self, expiry_key: &u64, map_key: &K) {
572 if let Some(list) = self.expiries.get_mut(expiry_key) {
573 list.remove(map_key);
574
575 if list.is_empty() {
576 self.expiries.remove(expiry_key);
577 }
578 }
579 }
580
581 #[inline(always)]
585 pub fn contains_key(&self, k: &K) -> bool {
586 self.get(k).is_some()
587 }
588
589 #[inline(always)]
593 pub fn contains_key_unchecked(&self, k: &K) -> bool {
594 self.map.keys().contains(k)
595 }
596}
597
598#[cfg(test)]
599#[cfg(not(feature = "std"))]
600mod tests {
601 use super::*;
602
603 struct MockClock {
604 current_time: u64,
605 }
606
607 impl Clock for MockClock {
608 fn elapsed_seconds_since_creation(&self) -> u64 {
609 self.current_time
610 }
611 }
612
613 #[test]
614 fn nostd_insert_and_get_constant_entry() {
615 let clock = MockClock { current_time: 1000 };
616 let mut map = TimedMap::new(clock);
617
618 map.insert_constant(1, "constant value");
619
620 assert_eq!(map.get(&1), Some(&"constant value"));
621 assert_eq!(map.get_remaining_duration(&1), None);
622 }
623
624 #[test]
625 fn nostd_insert_and_get_expirable_entry() {
626 let clock = MockClock { current_time: 1000 };
627 let mut map = TimedMap::new(clock);
628 let duration = Duration::from_secs(60);
629
630 map.insert_expirable(1, "expirable value", duration);
631
632 assert_eq!(map.get(&1), Some(&"expirable value"));
633 assert_eq!(map.get_remaining_duration(&1), Some(duration));
634 }
635
636 #[test]
637 fn nostd_expired_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);
644
645 let clock = MockClock { current_time: 1070 };
647 map.clock = clock;
648
649 assert_eq!(map.get(&1), None);
651 assert_eq!(map.get_remaining_duration(&1), None);
652 }
653
654 #[test]
655 fn nostd_remove_entry() {
656 let clock = MockClock { current_time: 1000 };
657 let mut map = TimedMap::new(clock);
658
659 map.insert_constant(1, "constant value");
660
661 assert_eq!(map.remove(&1), Some("constant value"));
662 assert_eq!(map.get(&1), None);
663 }
664
665 #[test]
666 fn nostd_drop_expired_entries() {
667 let clock = MockClock { current_time: 1000 };
668 let mut map = TimedMap::new(clock);
669
670 map.insert_expirable(1, "expirable value1", Duration::from_secs(50));
672 map.insert_expirable(2, "expirable value2", Duration::from_secs(70));
673 map.insert_constant(3, "constant value");
674
675 let clock = MockClock { current_time: 1055 };
677 map.clock = clock;
678
679 assert_eq!(map.get(&1), None);
681 assert_eq!(map.get(&2), Some(&"expirable value2"));
682 assert_eq!(map.get(&3), Some(&"constant value"));
683
684 let clock = MockClock { current_time: 1071 };
686 map.clock = clock;
687
688 assert_eq!(map.get(&1), None);
689 assert_eq!(map.get(&2), None);
690 assert_eq!(map.get(&3), Some(&"constant value"));
691 }
692
693 #[test]
694 fn nostd_update_existing_entry() {
695 let clock = MockClock { current_time: 1000 };
696 let mut map = TimedMap::new(clock);
697
698 map.insert_constant(1, "initial value");
699 assert_eq!(map.get(&1), Some(&"initial value"));
700
701 map.insert_expirable(1, "updated value", Duration::from_secs(15));
703 assert_eq!(map.get(&1), Some(&"updated value"));
704
705 let clock = MockClock { current_time: 1016 };
707 map.clock = clock;
708
709 assert_eq!(map.get(&1), None);
710 }
711
712 #[test]
713 fn nostd_update_expirable_entry_status() {
714 let clock = MockClock { current_time: 1000 };
715 let mut map = TimedMap::new(clock);
716
717 map.insert_constant(1, "initial value");
718 assert_eq!(map.get(&1), Some(&"initial value"));
719
720 map.update_expiration_status(1, Duration::from_secs(16))
722 .expect("entry update shouldn't fail");
723 assert_eq!(map.get(&1), Some(&"initial value"));
724
725 let clock = MockClock { current_time: 1017 };
727 map.clock = clock;
728 assert_eq!(map.get(&1), None);
729 }
730
731 #[test]
732 fn nostd_update_expirable_entry_status_with_previou_time() {
733 let clock = MockClock { current_time: 1000 };
734 let mut map = TimedMap::new(clock);
735
736 map.insert_expirable(1, "expirable value", Duration::from_secs(15));
738 map.update_expiration_status(1, Duration::from_secs(15))
739 .expect("entry update shouldn't fail");
740
741 assert_eq!(map.get(&1), Some(&"expirable value"));
743 assert!(map.expiries.contains_key(&1015));
744 }
745
746 #[test]
747 fn nostd_contains_key() {
748 let clock = MockClock { current_time: 1000 };
749 let mut map = TimedMap::new(clock);
750
751 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
753 assert!(map.contains_key(&1));
754 }
755}
756
757#[cfg(feature = "std")]
758#[cfg(test)]
759mod std_tests {
760 use core::ops::Add;
761
762 use super::*;
763
764 #[test]
765 fn std_expirable_and_constant_entries() {
766 let mut map = TimedMap::new();
767
768 map.insert_constant(1, "constant value");
769 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
770
771 assert_eq!(map.get(&1), Some(&"constant value"));
772 assert_eq!(map.get(&2), Some(&"expirable value"));
773
774 assert_eq!(map.get_remaining_duration(&1), None);
775 assert!(map.get_remaining_duration(&2).is_some());
776 }
777
778 #[test]
779 fn std_expired_entry_removal() {
780 let mut map = TimedMap::new();
781 let duration = Duration::from_secs(2);
782
783 map.insert_expirable(1, "expirable value", duration);
784
785 std::thread::sleep(Duration::from_secs(3));
787
788 assert_eq!(map.get(&1), None);
790 assert_eq!(map.get_remaining_duration(&1), None);
791 }
792
793 #[test]
794 fn std_remove_entry() {
795 let mut map = TimedMap::new();
796
797 map.insert_constant(1, "constant value");
798 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
799
800 assert_eq!(map.remove(&1), Some("constant value"));
801 assert_eq!(map.remove(&2), Some("expirable value"));
802
803 assert_eq!(map.get(&1), None);
804 assert_eq!(map.get(&2), None);
805 }
806
807 #[test]
808 fn std_drop_expired_entries() {
809 let mut map = TimedMap::new();
810
811 map.insert_expirable(1, "expirable value1", Duration::from_secs(2));
812 map.insert_expirable(2, "expirable value2", Duration::from_secs(4));
813
814 std::thread::sleep(Duration::from_secs(3));
816
817 assert_eq!(map.get(&1), None);
819 assert_eq!(map.get(&2), Some(&"expirable value2"));
820 }
821
822 #[test]
823 fn std_update_existing_entry() {
824 let mut map = TimedMap::new();
825
826 map.insert_constant(1, "initial value");
827 assert_eq!(map.get(&1), Some(&"initial value"));
828
829 map.insert_expirable(1, "updated value", Duration::from_secs(1));
831 assert_eq!(map.get(&1), Some(&"updated value"));
832
833 std::thread::sleep(Duration::from_secs(2));
834
835 assert_eq!(map.get(&1), None);
837 }
838
839 #[test]
840 fn std_insert_constant_and_expirable_combined() {
841 let mut map = TimedMap::new();
842
843 map.insert_constant(1, "constant value");
845 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
846
847 assert_eq!(map.get(&1), Some(&"constant value"));
849 assert_eq!(map.get(&2), Some(&"expirable value"));
850
851 std::thread::sleep(Duration::from_secs(3));
853
854 assert_eq!(map.get(&1), Some(&"constant value"));
856 assert_eq!(map.get(&2), None);
857 }
858
859 #[test]
860 fn std_expirable_entry_still_valid_before_expiration() {
861 let mut map = TimedMap::new();
862
863 map.insert_expirable(1, "expirable value", Duration::from_secs(3));
865
866 std::thread::sleep(Duration::from_secs(2));
868
869 assert_eq!(map.get(&1), Some(&"expirable value"));
871 assert!(map.get_remaining_duration(&1).unwrap().as_secs() == 1);
872 }
873
874 #[test]
875 fn std_length_functions() {
876 let mut map = TimedMap::new();
877
878 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
879 map.insert_expirable(2, "expirable value", Duration::from_secs(1));
880 map.insert_expirable(3, "expirable value", Duration::from_secs(3));
881 map.insert_expirable(4, "expirable value", Duration::from_secs(3));
882 map.insert_expirable(5, "expirable value", Duration::from_secs(3));
883 map.insert_expirable(6, "expirable value", Duration::from_secs(3));
884
885 std::thread::sleep(Duration::from_secs(2).add(Duration::from_millis(1)));
886
887 assert_eq!(map.len(), 4);
888 assert_eq!(map.len_expired(), 2);
889 assert_eq!(map.len_unchecked(), 6);
890 }
891
892 #[test]
893 fn std_update_expirable_entry() {
894 let mut map = TimedMap::new();
895
896 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
897 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
898
899 std::thread::sleep(Duration::from_secs(2));
900
901 assert!(!map.expiries.contains_key(&1));
902 assert!(map.expiries.contains_key(&5));
903 assert_eq!(map.get(&1), Some(&"expirable value"));
904 }
905
906 #[test]
907 fn std_update_expirable_entry_status() {
908 let mut map = TimedMap::new();
909
910 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
911 map.update_expiration_status(1, Duration::from_secs(5))
912 .expect("entry update shouldn't fail");
913
914 std::thread::sleep(Duration::from_secs(3));
915 assert!(!map.expiries.contains_key(&1));
916 assert!(map.expiries.contains_key(&5));
917 assert_eq!(map.get(&1), Some(&"expirable value"));
918 }
919
920 #[test]
921 fn std_update_expirable_entry_status_with_previou_time() {
922 let mut map = TimedMap::new();
923
924 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
926 map.update_expiration_status(1, Duration::from_secs(5))
927 .expect("entry update shouldn't fail");
928
929 assert_eq!(map.get(&1), Some(&"expirable value"));
931 assert!(map.expiries.contains_key(&5));
932 }
933
934 #[test]
935 fn std_contains_key() {
936 let mut map = TimedMap::new();
937
938 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
940 assert!(map.contains_key(&1));
941 }
942
943 #[test]
944 fn std_does_not_contain_key_anymore() {
945 let mut map = TimedMap::new();
946
947 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
949 std::thread::sleep(Duration::from_secs(2));
950 assert!(!map.contains_key(&1));
951 }
952
953 #[test]
954 fn std_contains_key_unchecked() {
955 let mut map = TimedMap::new();
956
957 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
958 std::thread::sleep(Duration::from_secs(2));
959 assert!(map.contains_key_unchecked(&1));
960 }
961}