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