1use crate::ArcDynHit;
2use std::mem;
3use std::ops::DerefMut;
4use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
5use std::time::{Duration, Instant};
6
7pub struct StaticHitCache {
9 data: Box<[ArcDynHit]>,
10}
11
12impl StaticHitCache {
13 pub fn new<H>(items: impl IntoIterator<Item = H>) -> Self
15 where
16 H: Into<ArcDynHit>,
17 {
18 Self {
19 data: items.into_iter().map(Into::into).collect(),
20 }
21 }
22
23 pub fn get(&self) -> &[ArcDynHit] {
25 &self.data
26 }
27}
28
29#[derive(Default, Debug)]
34pub struct HitCache {
35 data: RwLock<Data>,
36 strategy: Strategy,
37}
38
39impl HitCache {
40 pub fn max_age(mut self, age: Duration) -> Self {
43 self.strategy = Strategy::MaxAge(age);
44 self
45 }
46
47 pub fn clear(&self) {
49 let mut guard = self.write();
50 guard.hits.clear();
51 guard.created = None;
52 }
53
54 pub fn get(&self) -> Option<Guard<'_>> {
56 let guard = self.read();
57
58 if self.is_stale(&guard) {
59 return None;
60 }
61
62 Some(Guard(guard))
63 }
64
65 pub fn get_or<I, H>(&self, f: impl FnOnce() -> I) -> Guard<'_>
67 where
68 I: Iterator<Item = H>,
69 H: Into<ArcDynHit>,
70 {
71 if let Some(a) = self.get() {
72 return a;
73 };
74
75 self.set(f().map(Into::into));
76 Guard(self.read())
77 }
78
79 pub fn set(&self, hits: impl Iterator<Item = ArcDynHit>) {
81 let mut guard = self.write();
82 guard.hits.clear();
83 guard.hits.extend(hits);
84 guard.created = Some(Instant::now());
85 }
86
87 #[doc(hidden)]
89 pub fn _is_poisoned(&self) -> bool {
90 self.data.is_poisoned()
91 }
92
93 fn is_stale(&self, data: &Data) -> bool {
94 match self.strategy {
95 Strategy::Lazy => data.created.is_none(),
96 Strategy::MaxAge(max_age) => data.created.map(|c| c.elapsed() > max_age).unwrap_or(true),
97 }
98 }
99
100 fn read(&self) -> RwLockReadGuard<'_, Data> {
101 match self.data.read() {
102 Ok(g) => g,
103 Err(e) => {
104 drop(e);
105
106 drop(self.write());
108 self.read()
109 }
110 }
111 }
112
113 fn write(&self) -> RwLockWriteGuard<'_, Data> {
114 self.data.write().unwrap_or_else(|e| {
115 log::error!("cache rwlock was poisoned! resetting cache and continuing");
116
117 let mut guard = e.into_inner();
118 mem::take(guard.deref_mut());
119 self.data.clear_poison();
120
121 guard
122 })
123 }
124}
125
126#[derive(Debug, Default)]
127enum Strategy {
128 #[default]
129 Lazy,
130 MaxAge(Duration),
131}
132
133#[derive(Default, Debug)]
134struct Data {
135 hits: Vec<ArcDynHit>,
136 created: Option<Instant>,
137}
138
139#[derive(Debug)]
140pub struct Guard<'a>(RwLockReadGuard<'a, Data>);
141
142impl Guard<'_> {
143 pub fn get(&self) -> &[ArcDynHit] {
144 &self.0.hits
145 }
146}