mini_moka/unsync/
builder.rs

1use super::{Cache, Weigher};
2use crate::common::builder_utils;
3
4use std::{
5    collections::hash_map::RandomState,
6    hash::{BuildHasher, Hash},
7    marker::PhantomData,
8    time::Duration,
9};
10
11/// Builds a [`Cache`][cache-struct] with various configuration knobs.
12///
13/// [cache-struct]: ./struct.Cache.html
14///
15/// # Examples
16///
17/// ```rust
18/// use mini_moka::unsync::Cache;
19/// use std::time::Duration;
20///
21/// let mut cache = Cache::builder()
22///     // Max 10,000 elements
23///     .max_capacity(10_000)
24///     // Time to live (TTL): 30 minutes
25///     .time_to_live(Duration::from_secs(30 * 60))
26///     // Time to idle (TTI):  5 minutes
27///     .time_to_idle(Duration::from_secs( 5 * 60))
28///     // Create the cache.
29///     .build();
30///
31/// // This entry will expire after 5 minutes (TTI) if there is no get().
32/// cache.insert(0, "zero");
33///
34/// // This get() will extend the entry life for another 5 minutes.
35/// cache.get(&0);
36///
37/// // Even though we keep calling get(), the entry will expire
38/// // after 30 minutes (TTL) from the insert().
39/// ```
40///
41#[must_use]
42pub struct CacheBuilder<K, V, C> {
43    max_capacity: Option<u64>,
44    initial_capacity: Option<usize>,
45    weigher: Option<Weigher<K, V>>,
46    time_to_live: Option<Duration>,
47    time_to_idle: Option<Duration>,
48    cache_type: PhantomData<C>,
49}
50
51impl<K, V> Default for CacheBuilder<K, V, Cache<K, V, RandomState>>
52where
53    K: Eq + Hash,
54{
55    fn default() -> Self {
56        Self {
57            max_capacity: None,
58            initial_capacity: None,
59            weigher: None,
60            time_to_live: None,
61            time_to_idle: None,
62            cache_type: Default::default(),
63        }
64    }
65}
66
67impl<K, V> CacheBuilder<K, V, Cache<K, V, RandomState>>
68where
69    K: Eq + Hash,
70{
71    /// Construct a new `CacheBuilder` that will be used to build a `Cache` holding
72    /// up to `max_capacity` entries.
73    pub fn new(max_capacity: u64) -> Self {
74        Self {
75            max_capacity: Some(max_capacity),
76            ..Default::default()
77        }
78    }
79
80    /// Builds a `Cache<K, V>`.
81    ///
82    /// # Panics
83    ///
84    /// Panics if configured with either `time_to_live` or `time_to_idle` higher than
85    /// 1000 years. This is done to protect against overflow when computing key
86    /// expiration.
87    pub fn build(self) -> Cache<K, V, RandomState> {
88        let build_hasher = RandomState::default();
89        builder_utils::ensure_expirations_or_panic(self.time_to_live, self.time_to_idle);
90        Cache::with_everything(
91            self.max_capacity,
92            self.initial_capacity,
93            build_hasher,
94            self.weigher,
95            self.time_to_live,
96            self.time_to_idle,
97        )
98    }
99
100    /// Builds a `Cache<K, V, S>`, with the given `hasher`.
101    ///
102    /// # Panics
103    ///
104    /// Panics if configured with either `time_to_live` or `time_to_idle` higher than
105    /// 1000 years. This is done to protect against overflow when computing key
106    /// expiration.
107    pub fn build_with_hasher<S>(self, hasher: S) -> Cache<K, V, S>
108    where
109        S: BuildHasher + Clone,
110    {
111        builder_utils::ensure_expirations_or_panic(self.time_to_live, self.time_to_idle);
112        Cache::with_everything(
113            self.max_capacity,
114            self.initial_capacity,
115            hasher,
116            self.weigher,
117            self.time_to_live,
118            self.time_to_idle,
119        )
120    }
121}
122
123impl<K, V, C> CacheBuilder<K, V, C> {
124    /// Sets the max capacity of the cache.
125    pub fn max_capacity(self, max_capacity: u64) -> Self {
126        Self {
127            max_capacity: Some(max_capacity),
128            ..self
129        }
130    }
131
132    /// Sets the initial capacity (number of entries) of the cache.
133    pub fn initial_capacity(self, number_of_entries: usize) -> Self {
134        Self {
135            initial_capacity: Some(number_of_entries),
136            ..self
137        }
138    }
139
140    /// Sets the weigher closure of the cache.
141    ///
142    /// The closure should take `&K` and `&V` as the arguments and returns a `u32`
143    /// representing the relative size of the entry.
144    pub fn weigher(self, weigher: impl FnMut(&K, &V) -> u32 + 'static) -> Self {
145        Self {
146            weigher: Some(Box::new(weigher)),
147            ..self
148        }
149    }
150
151    /// Sets the time to live of the cache.
152    ///
153    /// A cached entry will be expired after the specified duration past from
154    /// `insert`.
155    ///
156    /// # Panics
157    ///
158    /// `CacheBuilder::build*` methods will panic if the given `duration` is longer
159    /// than 1000 years. This is done to protect against overflow when computing key
160    /// expiration.
161    pub fn time_to_live(self, duration: Duration) -> Self {
162        Self {
163            time_to_live: Some(duration),
164            ..self
165        }
166    }
167
168    /// Sets the time to idle of the cache.
169    ///
170    /// A cached entry will be expired after the specified duration past from `get`
171    /// or `insert`.
172    ///
173    /// # Panics
174    ///
175    /// `CacheBuilder::build*` methods will panic if the given `duration` is longer
176    /// than 1000 years. This is done to protect against overflow when computing key
177    /// expiration.
178    pub fn time_to_idle(self, duration: Duration) -> Self {
179        Self {
180            time_to_idle: Some(duration),
181            ..self
182        }
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::CacheBuilder;
189
190    use std::time::Duration;
191
192    #[test]
193    fn build_cache() {
194        // Cache<char, String>
195        let mut cache = CacheBuilder::new(100).build();
196        let policy = cache.policy();
197
198        assert_eq!(policy.max_capacity(), Some(100));
199        assert_eq!(policy.time_to_live(), None);
200        assert_eq!(policy.time_to_idle(), None);
201
202        cache.insert('a', "Alice");
203        assert_eq!(cache.get(&'a'), Some(&"Alice"));
204
205        let mut cache = CacheBuilder::new(100)
206            .time_to_live(Duration::from_secs(45 * 60))
207            .time_to_idle(Duration::from_secs(15 * 60))
208            .build();
209        let policy = cache.policy();
210
211        assert_eq!(policy.max_capacity(), Some(100));
212        assert_eq!(policy.time_to_live(), Some(Duration::from_secs(45 * 60)));
213        assert_eq!(policy.time_to_idle(), Some(Duration::from_secs(15 * 60)));
214
215        cache.insert('a', "Alice");
216        assert_eq!(cache.get(&'a'), Some(&"Alice"));
217    }
218
219    #[test]
220    #[should_panic(expected = "time_to_live is longer than 1000 years")]
221    fn build_cache_too_long_ttl() {
222        let thousand_years_secs: u64 = 1000 * 365 * 24 * 3600;
223        let builder: CacheBuilder<char, String, _> = CacheBuilder::new(100);
224        let duration = Duration::from_secs(thousand_years_secs);
225        builder
226            .time_to_live(duration + Duration::from_secs(1))
227            .build();
228    }
229
230    #[test]
231    #[should_panic(expected = "time_to_idle is longer than 1000 years")]
232    fn build_cache_too_long_tti() {
233        let thousand_years_secs: u64 = 1000 * 365 * 24 * 3600;
234        let builder: CacheBuilder<char, String, _> = CacheBuilder::new(100);
235        let duration = Duration::from_secs(thousand_years_secs);
236        builder
237            .time_to_idle(duration + Duration::from_secs(1))
238            .build();
239    }
240}