1use super::*;
2
3use crate::iter::{GenericMapIntoIter, GenericMapIter, GenericMapIterMut};
4
5macro_rules! cfg_std_feature {
6 ($($item:item)*) => {
7 $(
8 #[cfg(feature = "std")]
9 $item
10 )*
11 };
12}
13
14macro_rules! cfg_not_std_feature {
15 ($($item:item)*) => {
16 $(
17 #[cfg(not(feature = "std"))]
18 $item
19 )*
20 };
21}
22
23cfg_not_std_feature! {
24 pub trait GenericKey: Clone + Eq + Ord {}
27 impl<T: Clone + Eq + Ord> GenericKey for T {}
28}
29
30cfg_std_feature! {
31 pub trait GenericKey: Clone + Eq + Ord + Hash {}
34 impl<T: Clone + Eq + Ord + Hash> GenericKey for T {}
35}
36
37#[allow(clippy::enum_variant_names)]
45#[derive(Clone, Debug)]
46enum GenericMap<K, V> {
47 BTreeMap(BTreeMap<K, V>),
48 #[cfg(feature = "std")]
49 HashMap(HashMap<K, V>),
50 #[cfg(all(feature = "std", feature = "rustc-hash"))]
51 FxHashMap(FxHashMap<K, V>),
52}
53
54impl<K, V> Default for GenericMap<K, V> {
55 fn default() -> Self {
56 Self::BTreeMap(BTreeMap::default())
57 }
58}
59
60impl<K, V> GenericMap<K, V>
61where
62 K: GenericKey,
63{
64 #[inline(always)]
65 fn get(&self, k: &K) -> Option<&V> {
66 match self {
67 Self::BTreeMap(inner) => inner.get(k),
68 #[cfg(feature = "std")]
69 Self::HashMap(inner) => inner.get(k),
70 #[cfg(all(feature = "std", feature = "rustc-hash"))]
71 Self::FxHashMap(inner) => inner.get(k),
72 }
73 }
74
75 #[inline(always)]
76 fn get_mut(&mut self, k: &K) -> Option<&mut V> {
77 match self {
78 Self::BTreeMap(inner) => inner.get_mut(k),
79 #[cfg(feature = "std")]
80 Self::HashMap(inner) => inner.get_mut(k),
81 #[cfg(all(feature = "std", feature = "rustc-hash"))]
82 Self::FxHashMap(inner) => inner.get_mut(k),
83 }
84 }
85
86 #[inline(always)]
87 fn len(&self) -> usize {
88 match self {
89 Self::BTreeMap(inner) => inner.len(),
90 #[cfg(feature = "std")]
91 Self::HashMap(inner) => inner.len(),
92 #[cfg(all(feature = "std", feature = "rustc-hash"))]
93 Self::FxHashMap(inner) => inner.len(),
94 }
95 }
96
97 #[inline(always)]
98 fn keys(&self) -> Vec<K> {
99 match self {
100 Self::BTreeMap(inner) => inner.keys().cloned().collect(),
101 #[cfg(feature = "std")]
102 Self::HashMap(inner) => inner.keys().cloned().collect(),
103 #[cfg(all(feature = "std", feature = "rustc-hash"))]
104 Self::FxHashMap(inner) => inner.keys().cloned().collect(),
105 }
106 }
107
108 #[inline(always)]
109 fn is_empty(&self) -> bool {
110 match self {
111 Self::BTreeMap(inner) => inner.is_empty(),
112 #[cfg(feature = "std")]
113 Self::HashMap(inner) => inner.is_empty(),
114 #[cfg(all(feature = "std", feature = "rustc-hash"))]
115 Self::FxHashMap(inner) => inner.is_empty(),
116 }
117 }
118
119 #[inline(always)]
120 fn insert(&mut self, k: K, v: V) -> Option<V> {
121 match self {
122 Self::BTreeMap(inner) => inner.insert(k, v),
123 #[cfg(feature = "std")]
124 Self::HashMap(inner) => inner.insert(k, v),
125 #[cfg(all(feature = "std", feature = "rustc-hash"))]
126 Self::FxHashMap(inner) => inner.insert(k, v),
127 }
128 }
129
130 #[inline(always)]
131 fn clear(&mut self) {
132 match self {
133 Self::BTreeMap(inner) => inner.clear(),
134 #[cfg(feature = "std")]
135 Self::HashMap(inner) => inner.clear(),
136 #[cfg(all(feature = "std", feature = "rustc-hash"))]
137 Self::FxHashMap(inner) => inner.clear(),
138 }
139 }
140
141 #[inline(always)]
142 fn remove(&mut self, k: &K) -> Option<V> {
143 match self {
144 Self::BTreeMap(inner) => inner.remove(k),
145 #[cfg(feature = "std")]
146 Self::HashMap(inner) => inner.remove(k),
147 #[cfg(all(feature = "std", feature = "rustc-hash"))]
148 Self::FxHashMap(inner) => inner.remove(k),
149 }
150 }
151
152 fn iter(&self) -> GenericMapIter<'_, K, V> {
153 match self {
154 Self::BTreeMap(inner) => GenericMapIter::BTreeMap(inner.iter()),
155 #[cfg(feature = "std")]
156 Self::HashMap(inner) => GenericMapIter::HashMap(inner.iter()),
157 #[cfg(all(feature = "std", feature = "rustc-hash"))]
158 Self::FxHashMap(inner) => GenericMapIter::FxHashMap(inner.iter()),
159 }
160 }
161
162 fn into_iter(self) -> GenericMapIntoIter<K, V> {
163 match self {
164 Self::BTreeMap(inner) => GenericMapIntoIter::BTreeMap(inner.into_iter()),
165 #[cfg(feature = "std")]
166 Self::HashMap(inner) => GenericMapIntoIter::HashMap(inner.into_iter()),
167 #[cfg(all(feature = "std", feature = "rustc-hash"))]
168 Self::FxHashMap(inner) => GenericMapIntoIter::FxHashMap(inner.into_iter()),
169 }
170 }
171
172 fn iter_mut(&mut self) -> GenericMapIterMut<'_, K, V> {
173 match self {
174 Self::BTreeMap(inner) => GenericMapIterMut::BTreeMap(inner.iter_mut()),
175 #[cfg(feature = "std")]
176 Self::HashMap(inner) => GenericMapIterMut::HashMap(inner.iter_mut()),
177 #[cfg(all(feature = "std", feature = "rustc-hash"))]
178 Self::FxHashMap(inner) => GenericMapIterMut::FxHashMap(inner.iter_mut()),
179 }
180 }
181}
182
183#[cfg(feature = "std")]
185#[allow(clippy::enum_variant_names)]
186pub enum MapKind {
187 BTreeMap,
188 HashMap,
189 #[cfg(feature = "rustc-hash")]
190 FxHashMap,
191}
192
193#[derive(Clone, Debug)]
200pub struct TimedMap<K, V, #[cfg(feature = "std")] C = StdClock, #[cfg(not(feature = "std"))] C> {
201 clock: C,
202
203 map: GenericMap<K, ExpirableEntry<V>>,
204 expiries: BTreeMap<u64, BTreeSet<K>>,
205
206 expiration_tick: u16,
207 expiration_tick_cap: u16,
208}
209
210#[cfg(feature = "serde")]
211impl<K: serde::Serialize + Ord, V: serde::Serialize, C: Clock> serde::Serialize
212 for TimedMap<K, V, C>
213{
214 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215 where
216 S: serde::Serializer,
217 {
218 let now = self.clock.elapsed_seconds_since_creation();
219 match &self.map {
220 GenericMap::BTreeMap(inner) => {
221 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
222 serializer.collect_map(map)
223 }
224 #[cfg(feature = "std")]
225 GenericMap::HashMap(inner) => {
226 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
227 serializer.collect_map(map)
228 }
229 #[cfg(all(feature = "std", feature = "rustc-hash"))]
230 GenericMap::FxHashMap(inner) => {
231 let map = inner.iter().filter(|(_k, v)| !v.is_expired(now));
232 serializer.collect_map(map)
233 }
234 }
235 }
236}
237
238impl<'a, K, V, C> IntoIterator for &'a TimedMap<K, V, C>
239where
240 K: GenericKey,
241 C: Clock,
242{
243 type Item = (&'a K, &'a V);
244 type IntoIter = IntoIter<(&'a K, &'a V)>;
245
246 fn into_iter(self) -> Self::IntoIter {
247 let now = self.clock.elapsed_seconds_since_creation();
248 let items: Vec<(&K, &V)> = self
249 .map
250 .iter()
251 .filter_map(|(k, v)| {
252 if !v.is_expired(now) {
253 Some((k, v.value()))
254 } else {
255 None
256 }
257 })
258 .collect();
259 items.into_iter()
260 }
261}
262
263impl<'a, K, V, C> IntoIterator for &'a mut TimedMap<K, V, C>
264where
265 K: GenericKey,
266 C: Clock,
267{
268 type Item = (&'a K, &'a mut V);
269 type IntoIter = IntoIter<(&'a K, &'a mut V)>;
270
271 fn into_iter(self) -> Self::IntoIter {
272 let now = self.clock.elapsed_seconds_since_creation();
273 let items: Vec<(&K, &mut V)> = self
274 .map
275 .iter_mut()
276 .filter_map(|(k, v)| {
277 if !v.is_expired(now) {
278 Some((k, v.value_mut()))
279 } else {
280 None
281 }
282 })
283 .collect();
284 items.into_iter()
285 }
286}
287
288impl<K, V, C> IntoIterator for TimedMap<K, V, C>
289where
290 K: GenericKey,
291 C: Clock,
292{
293 type Item = (K, V);
294 type IntoIter = IntoIter<(K, V)>;
295
296 fn into_iter(self) -> Self::IntoIter {
297 let now = self.clock.elapsed_seconds_since_creation();
298 let items: Vec<(K, V)> = self
299 .map
300 .into_iter()
301 .filter_map(|(k, v)| {
302 if !v.is_expired(now) {
303 Some((k.clone(), v.owned_value()))
304 } else {
305 None
306 }
307 })
308 .collect();
309 items.into_iter()
310 }
311}
312
313impl<K, V, C> Default for TimedMap<K, V, C>
314where
315 C: Default,
316{
317 fn default() -> Self {
318 Self {
319 clock: Default::default(),
320
321 map: GenericMap::default(),
322 expiries: BTreeMap::default(),
323
324 expiration_tick: 0,
325 expiration_tick_cap: 1,
326 }
327 }
328}
329
330#[cfg(feature = "std")]
331impl<K, V> TimedMap<K, V, StdClock>
332where
333 K: GenericKey,
334{
335 pub fn new() -> Self {
337 Self::default()
338 }
339
340 pub fn new_with_map_kind(map_kind: MapKind) -> Self {
342 let map = match map_kind {
343 MapKind::BTreeMap => GenericMap::<K, ExpirableEntry<V>>::BTreeMap(BTreeMap::default()),
344 MapKind::HashMap => GenericMap::HashMap(HashMap::default()),
345 #[cfg(feature = "rustc-hash")]
346 MapKind::FxHashMap => GenericMap::FxHashMap(FxHashMap::default()),
347 };
348
349 Self {
350 map,
351
352 clock: StdClock::default(),
353 expiries: BTreeMap::default(),
354
355 expiration_tick: 0,
356 expiration_tick_cap: 1,
357 }
358 }
359}
360
361impl<K, V, C> TimedMap<K, V, C>
362where
363 C: Clock,
364 K: GenericKey,
365{
366 #[cfg(not(feature = "std"))]
370 pub fn new(clock: C) -> Self {
371 Self {
372 clock,
373 map: GenericMap::default(),
374 expiries: BTreeMap::default(),
375 expiration_tick: 0,
376 expiration_tick_cap: 1,
377 }
378 }
379
380 #[inline(always)]
391 pub fn expiration_tick_cap(mut self, expiration_tick_cap: u16) -> Self {
392 self.expiration_tick_cap = expiration_tick_cap;
393 self
394 }
395
396 pub fn get(&self, k: &K) -> Option<&V> {
400 self.map
401 .get(k)
402 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
403 .map(|v| v.value())
404 }
405
406 pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
410 self.map
411 .get_mut(k)
412 .filter(|v| !v.is_expired(self.clock.elapsed_seconds_since_creation()))
413 .map(|v| v.value_mut())
414 }
415
416 #[inline(always)]
420 pub fn get_unchecked(&self, k: &K) -> Option<&V> {
421 self.map.get(k).map(|v| v.value())
422 }
423
424 #[inline(always)]
429 pub fn get_mut_unchecked(&mut self, k: &K) -> Option<&mut V> {
430 self.map.get_mut(k).map(|v| v.value_mut())
431 }
432
433 pub fn get_remaining_duration(&self, k: &K) -> Option<Duration> {
437 match self.map.get(k) {
438 Some(v) => {
439 let now = self.clock.elapsed_seconds_since_creation();
440 if v.is_expired(now) {
441 return None;
442 }
443
444 v.remaining_duration(now)
445 }
446 None => None,
447 }
448 }
449
450 #[inline(always)]
454 pub fn len(&self) -> usize {
455 self.map.len() - self.len_expired()
456 }
457
458 #[inline(always)]
462 pub fn len_expired(&self) -> usize {
463 let now = self.clock.elapsed_seconds_since_creation();
464 self.expiries
465 .range(..=now)
466 .map(|(_exp, keys)| keys.len())
467 .sum()
468 }
469
470 #[inline(always)]
474 pub fn len_unchecked(&self) -> usize {
475 self.map.len()
476 }
477
478 #[inline(always)]
480 pub fn keys(&self) -> Vec<K> {
481 self.map.keys()
482 }
483
484 #[inline(always)]
486 pub fn is_empty(&self) -> bool {
487 self.map.is_empty()
488 }
489
490 #[inline(always)]
496 fn insert(&mut self, k: K, v: V, expires_at: Option<u64>) -> Option<V> {
497 let entry = ExpirableEntry::new(v, expires_at);
498 match self.map.insert(k.clone(), entry) {
499 Some(old) => {
500 if let EntryStatus::ExpiresAtSeconds(e) = old.status() {
502 self.drop_key_from_expiry(e, &k)
503 }
504
505 Some(old.owned_value())
506 }
507 None => None,
508 }
509 }
510
511 pub fn insert_expirable(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
520 self.expiration_tick += 1;
521
522 let now = self.clock.elapsed_seconds_since_creation();
523 if self.expiration_tick >= self.expiration_tick_cap {
524 self.drop_expired_entries_inner(now);
525 self.expiration_tick = 0;
526 }
527
528 let expires_at = now + duration.as_secs();
529
530 let res = self.insert(k.clone(), v, Some(expires_at));
531
532 self.expiries.entry(expires_at).or_default().insert(k);
533
534 res
535 }
536
537 pub fn insert_expirable_unchecked(&mut self, k: K, v: V, duration: Duration) -> Option<V> {
546 let now = self.clock.elapsed_seconds_since_creation();
547 let expires_at = now + duration.as_secs();
548
549 let res = self.insert(k.clone(), v, Some(expires_at));
550
551 self.expiries.entry(expires_at).or_default().insert(k);
552
553 res
554 }
555
556 pub fn insert_constant(&mut self, k: K, v: V) -> Option<V> {
565 self.expiration_tick += 1;
566
567 let now = self.clock.elapsed_seconds_since_creation();
568 if self.expiration_tick >= self.expiration_tick_cap {
569 self.drop_expired_entries_inner(now);
570 self.expiration_tick = 0;
571 }
572
573 self.insert(k, v, None)
574 }
575
576 pub fn insert_constant_unchecked(&mut self, k: K, v: V) -> Option<V> {
585 self.expiration_tick += 1;
586 self.insert(k, v, None)
587 }
588
589 #[inline(always)]
595 pub fn remove(&mut self, k: &K) -> Option<V> {
596 self.map
597 .remove(k)
598 .filter(|v| {
599 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
600 self.drop_key_from_expiry(expires_at_seconds, k);
601 }
602
603 !v.is_expired(self.clock.elapsed_seconds_since_creation())
604 })
605 .map(|v| v.owned_value())
606 }
607
608 #[inline(always)]
613 pub fn remove_unchecked(&mut self, k: &K) -> Option<V> {
614 self.map
615 .remove(k)
616 .filter(|v| {
617 if let EntryStatus::ExpiresAtSeconds(expires_at_seconds) = v.status() {
618 self.drop_key_from_expiry(expires_at_seconds, k);
619 }
620
621 true
622 })
623 .map(|v| v.owned_value())
624 }
625
626 #[inline(always)]
628 pub fn clear(&mut self) {
629 self.map.clear()
630 }
631
632 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
634 let now = self.clock.elapsed_seconds_since_creation();
635 self.map.iter().filter_map(move |(k, v)| {
636 if !v.is_expired(now) {
637 Some((k, v.value()))
638 } else {
639 None
640 }
641 })
642 }
643
644 pub fn iter_unchecked(&self) -> impl Iterator<Item = (&K, &V)> {
646 self.map.iter().map(|(k, v)| (k, v.value()))
647 }
648
649 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
651 let now = self.clock.elapsed_seconds_since_creation();
652 self.map.iter_mut().filter_map(move |(k, v)| {
653 if !v.is_expired(now) {
654 Some((k, v.value_mut()))
655 } else {
656 None
657 }
658 })
659 }
660
661 pub fn iter_mut_unchecked(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
663 self.map.iter_mut().map(|(k, v)| (k, v.value_mut()))
664 }
665
666 pub fn update_expiration_status(
671 &mut self,
672 key: K,
673 duration: Duration,
674 ) -> Result<Option<EntryStatus>, &'static str> {
675 match self.map.get_mut(&key) {
676 Some(entry) => {
677 let old_status = *entry.status();
678 let now = self.clock.elapsed_seconds_since_creation();
679 let expires_at = now + duration.as_secs();
680
681 entry.update_status(EntryStatus::ExpiresAtSeconds(expires_at));
682
683 if let EntryStatus::ExpiresAtSeconds(t) = &old_status {
684 self.drop_key_from_expiry(t, &key);
685 }
686 self.expiries
687 .entry(expires_at)
688 .or_default()
689 .insert(key.clone());
690
691 Ok(Some(old_status))
692 }
693 None => Err("entry not found"),
694 }
695 }
696
697 #[inline(always)]
702 pub fn drop_expired_entries(&mut self) -> Vec<(K, V)> {
703 let now = self.clock.elapsed_seconds_since_creation();
704 self.drop_expired_entries_inner(now)
705 }
706
707 fn drop_expired_entries_inner(&mut self, now: u64) -> Vec<(K, V)> {
708 let mut expired_entries = Vec::new();
709 while let Some((exp, keys)) = self.expiries.pop_first() {
711 if exp > now {
713 self.expiries.insert(exp, keys);
714 break;
715 }
716
717 for key in keys {
718 if let Some(value) = self.map.remove(&key) {
719 expired_entries.push((key, value.owned_value()));
720 }
721 }
722 }
723
724 expired_entries
725 }
726
727 fn drop_key_from_expiry(&mut self, expiry_key: &u64, map_key: &K) {
728 if let Some(list) = self.expiries.get_mut(expiry_key) {
729 list.remove(map_key);
730
731 if list.is_empty() {
732 self.expiries.remove(expiry_key);
733 }
734 }
735 }
736
737 #[inline(always)]
741 pub fn contains_key(&self, k: &K) -> bool {
742 self.get(k).is_some()
743 }
744
745 #[inline(always)]
749 pub fn contains_key_unchecked(&self, k: &K) -> bool {
750 self.get_unchecked(k).is_some()
751 }
752}
753
754#[cfg(test)]
755#[cfg(not(feature = "std"))]
756mod tests {
757 use super::*;
758
759 struct MockClock {
760 current_time: u64,
761 }
762
763 impl Clock for MockClock {
764 fn elapsed_seconds_since_creation(&self) -> u64 {
765 self.current_time
766 }
767 }
768
769 #[test]
770 fn nostd_insert_and_get_constant_entry() {
771 let clock = MockClock { current_time: 1000 };
772 let mut map = TimedMap::new(clock);
773
774 map.insert_constant(1, "constant value");
775
776 assert_eq!(map.get(&1), Some(&"constant value"));
777 assert_eq!(map.get_remaining_duration(&1), None);
778 }
779
780 #[test]
781 fn nostd_insert_and_get_expirable_entry() {
782 let clock = MockClock { current_time: 1000 };
783 let mut map = TimedMap::new(clock);
784 let duration = Duration::from_secs(60);
785
786 map.insert_expirable(1, "expirable value", duration);
787
788 assert_eq!(map.get(&1), Some(&"expirable value"));
789 assert_eq!(map.get_remaining_duration(&1), Some(duration));
790 }
791
792 #[test]
793 fn nostd_expired_entry() {
794 let clock = MockClock { current_time: 1000 };
795 let mut map = TimedMap::new(clock);
796 let duration = Duration::from_secs(60);
797
798 map.insert_expirable(1, "expirable value", duration);
800
801 let clock = MockClock { current_time: 1070 };
803 map.clock = clock;
804
805 assert_eq!(map.get(&1), None);
807 assert_eq!(map.get_remaining_duration(&1), None);
808 }
809
810 #[test]
811 fn nostd_remove_entry() {
812 let clock = MockClock { current_time: 1000 };
813 let mut map = TimedMap::new(clock);
814
815 map.insert_constant(1, "constant value");
816
817 assert_eq!(map.remove(&1), Some("constant value"));
818 assert_eq!(map.get(&1), None);
819 }
820
821 #[test]
822 fn nostd_drop_expired_entries() {
823 let clock = MockClock { current_time: 1000 };
824 let mut map = TimedMap::new(clock);
825
826 map.insert_expirable(1, "expirable value1", Duration::from_secs(50));
828 map.insert_expirable(2, "expirable value2", Duration::from_secs(70));
829 map.insert_constant(3, "constant value");
830
831 let clock = MockClock { current_time: 1055 };
833 map.clock = clock;
834
835 assert_eq!(map.get(&1), None);
837 assert_eq!(map.get(&2), Some(&"expirable value2"));
838 assert_eq!(map.get(&3), Some(&"constant value"));
839
840 let clock = MockClock { current_time: 1071 };
842 map.clock = clock;
843
844 assert_eq!(map.get(&1), None);
845 assert_eq!(map.get(&2), None);
846 assert_eq!(map.get(&3), Some(&"constant value"));
847 }
848
849 #[test]
850 fn nostd_update_existing_entry() {
851 let clock = MockClock { current_time: 1000 };
852 let mut map = TimedMap::new(clock);
853
854 map.insert_constant(1, "initial value");
855 assert_eq!(map.get(&1), Some(&"initial value"));
856
857 map.insert_expirable(1, "updated value", Duration::from_secs(15));
859 assert_eq!(map.get(&1), Some(&"updated value"));
860
861 let clock = MockClock { current_time: 1016 };
863 map.clock = clock;
864
865 assert_eq!(map.get(&1), None);
866 }
867
868 #[test]
869 fn nostd_update_expirable_entry_status() {
870 let clock = MockClock { current_time: 1000 };
871 let mut map = TimedMap::new(clock);
872
873 map.insert_constant(1, "initial value");
874 assert_eq!(map.get(&1), Some(&"initial value"));
875
876 map.update_expiration_status(1, Duration::from_secs(16))
878 .expect("entry update shouldn't fail");
879 assert_eq!(map.get(&1), Some(&"initial value"));
880
881 let clock = MockClock { current_time: 1017 };
883 map.clock = clock;
884 assert_eq!(map.get(&1), None);
885 }
886
887 #[test]
888 fn nostd_update_expirable_entry_status_with_previou_time() {
889 let clock = MockClock { current_time: 1000 };
890 let mut map = TimedMap::new(clock);
891
892 map.insert_expirable(1, "expirable value", Duration::from_secs(15));
894 map.update_expiration_status(1, Duration::from_secs(15))
895 .expect("entry update shouldn't fail");
896
897 assert_eq!(map.get(&1), Some(&"expirable value"));
899 assert!(map.expiries.contains_key(&1015));
900 }
901
902 #[test]
903 fn nostd_contains_key() {
904 let clock = MockClock { current_time: 1000 };
905 let mut map = TimedMap::new(clock);
906
907 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
909 assert!(map.contains_key(&1));
910 }
911}
912
913#[cfg(feature = "std")]
914#[cfg(test)]
915mod std_tests {
916 use core::ops::Add;
917
918 use super::*;
919
920 #[test]
921 fn std_expirable_and_constant_entries() {
922 let mut map = TimedMap::new();
923
924 map.insert_constant(1, "constant value");
925 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
926
927 assert_eq!(map.get(&1), Some(&"constant value"));
928 assert_eq!(map.get(&2), Some(&"expirable value"));
929
930 assert_eq!(map.get_remaining_duration(&1), None);
931 assert!(map.get_remaining_duration(&2).is_some());
932 }
933
934 #[test]
935 fn std_expired_entry_removal() {
936 let mut map = TimedMap::new();
937 let duration = Duration::from_secs(2);
938
939 map.insert_expirable(1, "expirable value", duration);
940
941 std::thread::sleep(Duration::from_secs(3));
943
944 assert_eq!(map.get(&1), None);
946 assert_eq!(map.get_remaining_duration(&1), None);
947 }
948
949 #[test]
950 fn std_remove_entry() {
951 let mut map = TimedMap::new();
952
953 map.insert_constant(1, "constant value");
954 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
955
956 assert_eq!(map.remove(&1), Some("constant value"));
957 assert_eq!(map.remove(&2), Some("expirable value"));
958
959 assert_eq!(map.get(&1), None);
960 assert_eq!(map.get(&2), None);
961 }
962
963 #[test]
964 fn std_drop_expired_entries() {
965 let mut map = TimedMap::new();
966
967 map.insert_expirable(1, "expirable value1", Duration::from_secs(2));
968 map.insert_expirable(2, "expirable value2", Duration::from_secs(4));
969
970 std::thread::sleep(Duration::from_secs(3));
972
973 assert_eq!(map.get(&1), None);
975 assert_eq!(map.get(&2), Some(&"expirable value2"));
976 }
977
978 #[test]
979 fn std_update_existing_entry() {
980 let mut map = TimedMap::new();
981
982 map.insert_constant(1, "initial value");
983 assert_eq!(map.get(&1), Some(&"initial value"));
984
985 map.insert_expirable(1, "updated value", Duration::from_secs(1));
987 assert_eq!(map.get(&1), Some(&"updated value"));
988
989 std::thread::sleep(Duration::from_secs(2));
990
991 assert_eq!(map.get(&1), None);
993 }
994
995 #[test]
996 fn std_insert_constant_and_expirable_combined() {
997 let mut map = TimedMap::new();
998
999 map.insert_constant(1, "constant value");
1001 map.insert_expirable(2, "expirable value", Duration::from_secs(2));
1002
1003 assert_eq!(map.get(&1), Some(&"constant value"));
1005 assert_eq!(map.get(&2), Some(&"expirable value"));
1006
1007 std::thread::sleep(Duration::from_secs(3));
1009
1010 assert_eq!(map.get(&1), Some(&"constant value"));
1012 assert_eq!(map.get(&2), None);
1013 }
1014
1015 #[test]
1016 fn std_expirable_entry_still_valid_before_expiration() {
1017 let mut map = TimedMap::new();
1018
1019 map.insert_expirable(1, "expirable value", Duration::from_secs(3));
1021
1022 std::thread::sleep(Duration::from_secs(2));
1024
1025 assert_eq!(map.get(&1), Some(&"expirable value"));
1027 assert!(map.get_remaining_duration(&1).unwrap().as_secs() == 1);
1028 }
1029
1030 #[test]
1031 fn std_length_functions() {
1032 let mut map = TimedMap::new();
1033
1034 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1035 map.insert_expirable(2, "expirable value", Duration::from_secs(1));
1036 map.insert_expirable(3, "expirable value", Duration::from_secs(3));
1037 map.insert_expirable(4, "expirable value", Duration::from_secs(3));
1038 map.insert_expirable(5, "expirable value", Duration::from_secs(3));
1039 map.insert_expirable(6, "expirable value", Duration::from_secs(3));
1040
1041 std::thread::sleep(Duration::from_secs(2).add(Duration::from_millis(1)));
1042
1043 assert_eq!(map.len(), 4);
1044 assert_eq!(map.len_expired(), 2);
1045 assert_eq!(map.len_unchecked(), 6);
1046 }
1047
1048 #[test]
1049 fn std_update_expirable_entry() {
1050 let mut map = TimedMap::new();
1051
1052 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1053 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1054
1055 std::thread::sleep(Duration::from_secs(2));
1056
1057 assert!(!map.expiries.contains_key(&1));
1058 assert!(map.expiries.contains_key(&5));
1059 assert_eq!(map.get(&1), Some(&"expirable value"));
1060 }
1061
1062 #[test]
1063 fn std_update_expirable_entry_status() {
1064 let mut map = TimedMap::new();
1065
1066 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1067 map.update_expiration_status(1, Duration::from_secs(5))
1068 .expect("entry update shouldn't fail");
1069
1070 std::thread::sleep(Duration::from_secs(3));
1071 assert!(!map.expiries.contains_key(&1));
1072 assert!(map.expiries.contains_key(&5));
1073 assert_eq!(map.get(&1), Some(&"expirable value"));
1074 }
1075
1076 #[test]
1077 fn std_update_expirable_entry_status_with_previou_time() {
1078 let mut map = TimedMap::new();
1079
1080 map.insert_expirable(1, "expirable value", Duration::from_secs(5));
1082 map.update_expiration_status(1, Duration::from_secs(5))
1083 .expect("entry update shouldn't fail");
1084
1085 assert_eq!(map.get(&1), Some(&"expirable value"));
1087 assert!(map.expiries.contains_key(&5));
1088 }
1089
1090 #[test]
1091 fn std_contains_key() {
1092 let mut map = TimedMap::new();
1093
1094 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1096 assert!(map.contains_key(&1));
1097 }
1098
1099 #[test]
1100 fn std_does_not_contain_key_anymore() {
1101 let mut map = TimedMap::new();
1102
1103 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1105 std::thread::sleep(Duration::from_secs(2));
1106 assert!(!map.contains_key(&1));
1107 }
1108
1109 #[test]
1110 fn std_contains_key_unchecked() {
1111 let mut map = TimedMap::new();
1112
1113 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1114 std::thread::sleep(Duration::from_secs(2));
1115 assert!(map.contains_key_unchecked(&1));
1116 }
1117
1118 #[test]
1119 #[cfg(feature = "serde")]
1120 fn ignore_expired_entries_on_serialize() {
1121 let mut map = TimedMap::new();
1122
1123 map.insert_expirable(1, "expirable value", Duration::from_secs(1));
1124 map.insert_expirable(2, "expirable value2", Duration::from_secs(5));
1125 std::thread::sleep(Duration::from_secs(2));
1126
1127 let actual = serde_json::json!(map);
1128 let expected = serde_json::json!({
1129 "2": "expirable value2"
1130 });
1131
1132 assert_eq!(expected, actual);
1133 }
1134}