moka/sync/entry_selector.rs
1use equivalent::Equivalent;
2
3use crate::{ops::compute, Entry};
4
5use super::Cache;
6
7use std::{
8 hash::{BuildHasher, Hash},
9 sync::Arc,
10};
11
12/// Provides advanced methods to select or insert an entry of the cache.
13///
14/// Many methods here return an [`Entry`], a snapshot of a single key-value pair in
15/// the cache, carrying additional information like `is_fresh`.
16///
17/// `OwnedKeyEntrySelector` is constructed from the [`entry`][entry-method] method on
18/// the cache.
19///
20/// [`Entry`]: ../struct.Entry.html
21/// [entry-method]: ./struct.Cache.html#method.entry
22pub struct OwnedKeyEntrySelector<'a, K, V, S> {
23 owned_key: K,
24 hash: u64,
25 cache: &'a Cache<K, V, S>,
26}
27
28impl<'a, K, V, S> OwnedKeyEntrySelector<'a, K, V, S>
29where
30 K: Hash + Eq + Send + Sync + 'static,
31 V: Clone + Send + Sync + 'static,
32 S: BuildHasher + Clone + Send + Sync + 'static,
33{
34 pub(crate) fn new(owned_key: K, hash: u64, cache: &'a Cache<K, V, S>) -> Self {
35 Self {
36 owned_key,
37 hash,
38 cache,
39 }
40 }
41
42 /// Performs a compute operation on a cached entry by using the given closure
43 /// `f`. A compute operation is either put, remove or no-operation (nop).
44 ///
45 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
46 /// the key, and return an `ops::compute::Op<V>` enum.
47 ///
48 /// This method works as the followings:
49 ///
50 /// 1. Apply the closure `f` to the current cached `Entry`, and get an
51 /// `ops::compute::Op<V>`.
52 /// 2. Execute the op on the cache:
53 /// - `Op::Put(V)`: Put the new value `V` to the cache.
54 /// - `Op::Remove`: Remove the current cached entry.
55 /// - `Op::Nop`: Do nothing.
56 /// 3. Return an `ops::compute::CompResult<K, V>` as the followings:
57 ///
58 /// | [`Op<V>`] | [`Entry<K, V>`] already exists? | [`CompResult<K, V>`] | Notes |
59 /// |:--------- |:--- |:--------------------------- |:------------------------------- |
60 /// | `Put(V)` | no | `Inserted(Entry<K, V>)` | The new entry is returned. |
61 /// | `Put(V)` | yes | `ReplacedWith(Entry<K, V>)` | The new entry is returned. |
62 /// | `Remove` | no | `StillNone(Arc<K>)` | |
63 /// | `Remove` | yes | `Removed(Entry<K, V>)` | The removed entry is returned. |
64 /// | `Nop` | no | `StillNone(Arc<K>)` | |
65 /// | `Nop` | yes | `Unchanged(Entry<K, V>)` | The existing entry is returned. |
66 ///
67 /// # See Also
68 ///
69 /// - If you want the `Future` resolve to `Result<Op<V>>` instead of `Op<V>`, and
70 /// modify entry only when resolved to `Ok(V)`, use the
71 /// [`and_try_compute_with`] method.
72 /// - If you only want to update or insert, use the [`and_upsert_with`] method.
73 ///
74 /// [`Entry<K, V>`]: ../struct.Entry.html
75 /// [`Op<V>`]: ../ops/compute/enum.Op.html
76 /// [`CompResult<K, V>`]: ../ops/compute/enum.CompResult.html
77 /// [`and_upsert_with`]: #method.and_upsert_with
78 /// [`and_try_compute_with`]: #method.and_try_compute_with
79 ///
80 /// # Example
81 ///
82 /// ```rust
83 /// use moka::{
84 /// sync::Cache,
85 /// ops::compute::{CompResult, Op},
86 /// };
87 ///
88 /// let cache: Cache<String, u64> = Cache::new(100);
89 /// let key = "key1".to_string();
90 ///
91 /// /// Increment a cached `u64` counter. If the counter is greater than or
92 /// /// equal to 2, remove it.
93 /// fn inclement_or_remove_counter(
94 /// cache: &Cache<String, u64>,
95 /// key: &str,
96 /// ) -> CompResult<String, u64> {
97 /// cache
98 /// .entry(key.to_string())
99 /// .and_compute_with(|maybe_entry| {
100 /// if let Some(entry) = maybe_entry {
101 /// let counter = entry.into_value();
102 /// if counter < 2 {
103 /// Op::Put(counter.saturating_add(1)) // Update
104 /// } else {
105 /// Op::Remove
106 /// }
107 /// } else {
108 /// Op::Put(1) // Insert
109 /// }
110 /// })
111 /// }
112 ///
113 /// // This should insert a new counter value 1 to the cache, and return the
114 /// // value with the kind of the operation performed.
115 /// let result = inclement_or_remove_counter(&cache, &key);
116 /// let CompResult::Inserted(entry) = result else {
117 /// panic!("`Inserted` should be returned: {result:?}");
118 /// };
119 /// assert_eq!(entry.into_value(), 1);
120 ///
121 /// // This should increment the cached counter value by 1.
122 /// let result = inclement_or_remove_counter(&cache, &key);
123 /// let CompResult::ReplacedWith(entry) = result else {
124 /// panic!("`ReplacedWith` should be returned: {result:?}");
125 /// };
126 /// assert_eq!(entry.into_value(), 2);
127 ///
128 /// // This should remove the cached counter from the cache, and returns the
129 /// // _removed_ value.
130 /// let result = inclement_or_remove_counter(&cache, &key);
131 /// let CompResult::Removed(entry) = result else {
132 /// panic!("`Removed` should be returned: {result:?}");
133 /// };
134 /// assert_eq!(entry.into_value(), 2);
135 ///
136 /// // The key should no longer exist.
137 /// assert!(!cache.contains_key(&key));
138 ///
139 /// // This should start over; insert a new counter value 1 to the cache.
140 /// let result = inclement_or_remove_counter(&cache, &key);
141 /// let CompResult::Inserted(entry) = result else {
142 /// panic!("`Inserted` should be returned: {result:?}");
143 /// };
144 /// assert_eq!(entry.into_value(), 1);
145 /// ```
146 ///
147 /// # Concurrent calls on the same key
148 ///
149 /// This method guarantees that concurrent calls on the same key are executed
150 /// serially. That is, `and_compute_with` calls on the same key never run
151 /// concurrently. The calls are serialized by the order of their invocation. It
152 /// uses a key-level lock to achieve this.
153 pub fn and_compute_with<F>(self, f: F) -> compute::CompResult<K, V>
154 where
155 F: FnOnce(Option<Entry<K, V>>) -> compute::Op<V>,
156 {
157 let key = Arc::new(self.owned_key);
158 self.cache.compute_with_hash_and_fun(key, self.hash, f)
159 }
160
161 /// Performs a compute operation on a cached entry by using the given closure
162 /// `f`. A compute operation is either put, remove or no-operation (nop).
163 ///
164 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
165 /// the key, and return a `Result<ops::compute::Op<V>, E>`.
166 ///
167 /// This method works as the followings:
168 ///
169 /// 1. Apply the closure `f` to the current cached `Entry`, and get a
170 /// `Result<ops::compute::Op<V>, E>`.
171 /// 2. If resolved to `Err(E)`, return it.
172 /// 3. Else, execute the op on the cache:
173 /// - `Ok(Op::Put(V))`: Put the new value `V` to the cache.
174 /// - `Ok(Op::Remove)`: Remove the current cached entry.
175 /// - `Ok(Op::Nop)`: Do nothing.
176 /// 4. Return an `Ok(ops::compute::CompResult<K, V>)` as the followings:
177 ///
178 /// | [`Op<V>`] | [`Entry<K, V>`] already exists? | [`CompResult<K, V>`] | Notes |
179 /// |:--------- |:--- |:--------------------------- |:------------------------------- |
180 /// | `Put(V)` | no | `Inserted(Entry<K, V>)` | The new entry is returned. |
181 /// | `Put(V)` | yes | `ReplacedWith(Entry<K, V>)` | The new entry is returned. |
182 /// | `Remove` | no | `StillNone(Arc<K>)` | |
183 /// | `Remove` | yes | `Removed(Entry<K, V>)` | The removed entry is returned. |
184 /// | `Nop` | no | `StillNone(Arc<K>)` | |
185 /// | `Nop` | yes | `Unchanged(Entry<K, V>)` | The existing entry is returned. |
186 ///
187 /// # See Also
188 ///
189 /// - If you want the `Future` resolve to `Op<V>` instead of `Result<Op<V>>`, use
190 /// the [`and_compute_with`] method.
191 /// - If you only want to put, use the [`and_upsert_with`] method.
192 ///
193 /// [`Entry<K, V>`]: ../struct.Entry.html
194 /// [`Op<V>`]: ../ops/compute/enum.Op.html
195 /// [`CompResult<K, V>`]: ../ops/compute/enum.CompResult.html
196 /// [`and_upsert_with`]: #method.and_upsert_with
197 /// [`and_compute_with`]: #method.and_compute_with
198 ///
199 /// # Example
200 ///
201 /// See [`try_append_value_async.rs`] in the `examples` directory.
202 ///
203 /// [`try_append_value_sync.rs`]:
204 /// https://github.com/moka-rs/moka/tree/main/examples/try_append_value_sync.rs
205 ///
206 /// # Concurrent calls on the same key
207 ///
208 /// This method guarantees that concurrent calls on the same key are executed
209 /// serially. That is, `and_try_compute_with` calls on the same key never run
210 /// concurrently. The calls are serialized by the order of their invocation. It
211 /// uses a key-level lock to achieve this.
212 pub fn and_try_compute_with<F, E>(self, f: F) -> Result<compute::CompResult<K, V>, E>
213 where
214 F: FnOnce(Option<Entry<K, V>>) -> Result<compute::Op<V>, E>,
215 E: Send + Sync + 'static,
216 {
217 let key = Arc::new(self.owned_key);
218 self.cache.try_compute_with_hash_and_fun(key, self.hash, f)
219 }
220
221 /// Performs an upsert of an [`Entry`] by using the given closure `f`. The word
222 /// "upsert" here means "update" or "insert".
223 ///
224 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
225 /// the key, and return a new value `V`.
226 ///
227 /// This method works as the followings:
228 ///
229 /// 1. Apply the closure `f` to the current cached `Entry`, and get a new value
230 /// `V`.
231 /// 2. Upsert the new value to the cache.
232 /// 3. Return the `Entry` having the upserted value.
233 ///
234 /// # See Also
235 ///
236 /// - If you want to optionally upsert, that is to upsert only when certain
237 /// conditions meet, use the [`and_compute_with`] method.
238 /// - If you try to upsert, that is to make the `Future` resolve to `Result<V>`
239 /// instead of `V`, and upsert only when resolved to `Ok(V)`, use the
240 /// [`and_try_compute_with`] method.
241 ///
242 /// [`Entry`]: ../struct.Entry.html
243 /// [`and_compute_with`]: #method.and_compute_with
244 /// [`and_try_compute_with`]: #method.and_try_compute_with
245 ///
246 /// # Example
247 ///
248 /// ```rust
249 /// use moka::sync::Cache;
250 ///
251 /// let cache: Cache<String, u64> = Cache::new(100);
252 /// let key = "key1".to_string();
253 ///
254 /// let entry = cache
255 /// .entry(key.clone())
256 /// .and_upsert_with(|maybe_entry| {
257 /// if let Some(entry) = maybe_entry {
258 /// entry.into_value().saturating_add(1) // Update
259 /// } else {
260 /// 1 // Insert
261 /// }
262 /// });
263 /// // It was not an update.
264 /// assert!(!entry.is_old_value_replaced());
265 /// assert_eq!(entry.key(), &key);
266 /// assert_eq!(entry.into_value(), 1);
267 ///
268 /// let entry = cache
269 /// .entry(key.clone())
270 /// .and_upsert_with(|maybe_entry| {
271 /// if let Some(entry) = maybe_entry {
272 /// entry.into_value().saturating_add(1)
273 /// } else {
274 /// 1
275 /// }
276 /// });
277 /// // It was an update.
278 /// assert!(entry.is_old_value_replaced());
279 /// assert_eq!(entry.key(), &key);
280 /// assert_eq!(entry.into_value(), 2);
281 /// ```
282 ///
283 /// # Concurrent calls on the same key
284 ///
285 /// This method guarantees that concurrent calls on the same key are executed
286 /// serially. That is, `and_upsert_with` calls on the same key never run
287 /// concurrently. The calls are serialized by the order of their invocation. It
288 /// uses a key-level lock to achieve this.
289 pub fn and_upsert_with<F>(self, f: F) -> Entry<K, V>
290 where
291 F: FnOnce(Option<Entry<K, V>>) -> V,
292 {
293 let key = Arc::new(self.owned_key);
294 self.cache.upsert_with_hash_and_fun(key, self.hash, f)
295 }
296
297 /// Returns the corresponding [`Entry`] for the key given when this entry
298 /// selector was constructed. If the entry does not exist, inserts one by calling
299 /// the [`default`][std-default-function] function of the value type `V`.
300 ///
301 /// [`Entry`]: ../struct.Entry.html
302 /// [std-default-function]: https://doc.rust-lang.org/stable/std/default/trait.Default.html#tymethod.default
303 ///
304 /// # Example
305 ///
306 /// ```rust
307 /// use moka::sync::Cache;
308 ///
309 /// let cache: Cache<String, Option<u32>> = Cache::new(100);
310 /// let key = "key1".to_string();
311 ///
312 /// let entry = cache.entry(key.clone()).or_default();
313 /// assert!(entry.is_fresh());
314 /// assert_eq!(entry.key(), &key);
315 /// assert_eq!(entry.into_value(), None);
316 ///
317 /// let entry = cache.entry(key).or_default();
318 /// // Not fresh because the value was already in the cache.
319 /// assert!(!entry.is_fresh());
320 /// ```
321 pub fn or_default(self) -> Entry<K, V>
322 where
323 V: Default,
324 {
325 let key = Arc::new(self.owned_key);
326 self.cache
327 .get_or_insert_with_hash(key, self.hash, Default::default)
328 }
329
330 /// Returns the corresponding [`Entry`] for the key given when this entry
331 /// selector was constructed. If the entry does not exist, inserts one by using
332 /// the the given `default` value for `V`.
333 ///
334 /// [`Entry`]: ../struct.Entry.html
335 ///
336 /// # Example
337 ///
338 /// ```rust
339 /// use moka::sync::Cache;
340 ///
341 /// let cache: Cache<String, u32> = Cache::new(100);
342 /// let key = "key1".to_string();
343 ///
344 /// let entry = cache.entry(key.clone()).or_insert(3);
345 /// assert!(entry.is_fresh());
346 /// assert_eq!(entry.key(), &key);
347 /// assert_eq!(entry.into_value(), 3);
348 ///
349 /// let entry = cache.entry(key).or_insert(6);
350 /// // Not fresh because the value was already in the cache.
351 /// assert!(!entry.is_fresh());
352 /// assert_eq!(entry.into_value(), 3);
353 /// ```
354 pub fn or_insert(self, default: V) -> Entry<K, V> {
355 let key = Arc::new(self.owned_key);
356 let init = || default;
357 self.cache.get_or_insert_with_hash(key, self.hash, init)
358 }
359
360 /// Returns the corresponding [`Entry`] for the key given when this entry
361 /// selector was constructed. If the entry does not exist, evaluates the `init`
362 /// closure and inserts the output.
363 ///
364 /// [`Entry`]: ../struct.Entry.html
365 ///
366 /// # Example
367 ///
368 /// ```rust
369 /// use moka::sync::Cache;
370 ///
371 /// let cache: Cache<String, String> = Cache::new(100);
372 /// let key = "key1".to_string();
373 ///
374 /// let entry = cache
375 /// .entry(key.clone())
376 /// .or_insert_with(|| "value1".to_string());
377 /// assert!(entry.is_fresh());
378 /// assert_eq!(entry.key(), &key);
379 /// assert_eq!(entry.into_value(), "value1");
380 ///
381 /// let entry = cache
382 /// .entry(key)
383 /// .or_insert_with(|| "value2".to_string());
384 /// // Not fresh because the value was already in the cache.
385 /// assert!(!entry.is_fresh());
386 /// assert_eq!(entry.into_value(), "value1");
387 /// ```
388 ///
389 /// # Concurrent calls on the same key
390 ///
391 /// This method guarantees that concurrent calls on the same not-existing entry
392 /// are coalesced into one evaluation of the `init` closure. Only one of the
393 /// calls evaluates its closure (thus returned entry's `is_fresh` method returns
394 /// `true`), and other calls wait for that closure to complete (and their
395 /// `is_fresh` return `false`).
396 ///
397 /// For more detail about the coalescing behavior, see
398 /// [`Cache::get_with`][get-with-method].
399 ///
400 /// [get-with-method]: ./struct.Cache.html#method.get_with
401 pub fn or_insert_with(self, init: impl FnOnce() -> V) -> Entry<K, V> {
402 let key = Arc::new(self.owned_key);
403 let replace_if = None as Option<fn(&V) -> bool>;
404 self.cache
405 .get_or_insert_with_hash_and_fun(key, self.hash, init, replace_if, true)
406 }
407
408 /// Works like [`or_insert_with`](#method.or_insert_with), but takes an additional
409 /// `replace_if` closure.
410 ///
411 /// This method will evaluate the `init` closure and insert the output to the
412 /// cache when:
413 ///
414 /// - The key does not exist.
415 /// - Or, `replace_if` closure returns `true`.
416 pub fn or_insert_with_if(
417 self,
418 init: impl FnOnce() -> V,
419 replace_if: impl FnMut(&V) -> bool,
420 ) -> Entry<K, V> {
421 let key = Arc::new(self.owned_key);
422 self.cache
423 .get_or_insert_with_hash_and_fun(key, self.hash, init, Some(replace_if), true)
424 }
425
426 /// Returns the corresponding [`Entry`] for the key given when this entry
427 /// selector was constructed. If the entry does not exist, evaluates the `init`
428 /// closure, and inserts an entry if `Some(value)` was returned. If `None` was
429 /// returned from the closure, this method does not insert an entry and returns
430 /// `None`.
431 ///
432 /// [`Entry`]: ../struct.Entry.html
433 ///
434 /// # Example
435 ///
436 /// ```rust
437 /// use moka::sync::Cache;
438 ///
439 /// let cache: Cache<String, u32> = Cache::new(100);
440 /// let key = "key1".to_string();
441 ///
442 /// let none_entry = cache
443 /// .entry(key.clone())
444 /// .or_optionally_insert_with(|| None);
445 /// assert!(none_entry.is_none());
446 ///
447 /// let some_entry = cache
448 /// .entry(key.clone())
449 /// .or_optionally_insert_with(|| Some(3));
450 /// assert!(some_entry.is_some());
451 /// let entry = some_entry.unwrap();
452 /// assert!(entry.is_fresh());
453 /// assert_eq!(entry.key(), &key);
454 /// assert_eq!(entry.into_value(), 3);
455 ///
456 /// let some_entry = cache
457 /// .entry(key)
458 /// .or_optionally_insert_with(|| Some(6));
459 /// let entry = some_entry.unwrap();
460 /// // Not fresh because the value was already in the cache.
461 /// assert!(!entry.is_fresh());
462 /// assert_eq!(entry.into_value(), 3);
463 /// ```
464 ///
465 /// # Concurrent calls on the same key
466 ///
467 /// This method guarantees that concurrent calls on the same not-existing entry
468 /// are coalesced into one evaluation of the `init` closure. Only one of the
469 /// calls evaluates its closure (thus returned entry's `is_fresh` method returns
470 /// `true`), and other calls wait for that closure to complete (and their
471 /// `is_fresh` return `false`).
472 ///
473 /// For more detail about the coalescing behavior, see
474 /// [`Cache::optionally_get_with`][opt-get-with-method].
475 ///
476 /// [opt-get-with-method]: ./struct.Cache.html#method.optionally_get_with
477 pub fn or_optionally_insert_with(
478 self,
479 init: impl FnOnce() -> Option<V>,
480 ) -> Option<Entry<K, V>> {
481 let key = Arc::new(self.owned_key);
482 self.cache
483 .get_or_optionally_insert_with_hash_and_fun(key, self.hash, init, true)
484 }
485
486 /// Returns the corresponding [`Entry`] for the key given when this entry
487 /// selector was constructed. If the entry does not exist, evaluates the `init`
488 /// closure, and inserts an entry if `Ok(value)` was returned. If `Err(_)` was
489 /// returned from the closure, this method does not insert an entry and returns
490 /// the `Err` wrapped by [`std::sync::Arc`][std-arc].
491 ///
492 /// [`Entry`]: ../struct.Entry.html
493 /// [std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
494 ///
495 /// # Example
496 ///
497 /// ```rust
498 /// use moka::sync::Cache;
499 ///
500 /// let cache: Cache<String, u32> = Cache::new(100);
501 /// let key = "key1".to_string();
502 ///
503 /// let error_entry = cache
504 /// .entry(key.clone())
505 /// .or_try_insert_with(|| Err("error"));
506 /// assert!(error_entry.is_err());
507 ///
508 /// let ok_entry = cache
509 /// .entry(key.clone())
510 /// .or_try_insert_with(|| Ok::<u32, &str>(3));
511 /// assert!(ok_entry.is_ok());
512 /// let entry = ok_entry.unwrap();
513 /// assert!(entry.is_fresh());
514 /// assert_eq!(entry.key(), &key);
515 /// assert_eq!(entry.into_value(), 3);
516 ///
517 /// let ok_entry = cache
518 /// .entry(key)
519 /// .or_try_insert_with(|| Ok::<u32, &str>(6));
520 /// let entry = ok_entry.unwrap();
521 /// // Not fresh because the value was already in the cache.
522 /// assert!(!entry.is_fresh());
523 /// assert_eq!(entry.into_value(), 3);
524 /// ```
525 ///
526 /// # Concurrent calls on the same key
527 ///
528 /// This method guarantees that concurrent calls on the same not-existing entry
529 /// are coalesced into one evaluation of the `init` closure (as long as these
530 /// closures return the same error type). Only one of the calls evaluates its
531 /// closure (thus returned entry's `is_fresh` method returns `true`), and other
532 /// calls wait for that closure to complete (and their `is_fresh` return
533 /// `false`).
534 ///
535 /// For more detail about the coalescing behavior, see
536 /// [`Cache::try_get_with`][try-get-with-method].
537 ///
538 /// [try-get-with-method]: ./struct.Cache.html#method.try_get_with
539 pub fn or_try_insert_with<F, E>(self, init: F) -> Result<Entry<K, V>, Arc<E>>
540 where
541 F: FnOnce() -> Result<V, E>,
542 E: Send + Sync + 'static,
543 {
544 let key = Arc::new(self.owned_key);
545 self.cache
546 .get_or_try_insert_with_hash_and_fun(key, self.hash, init, true)
547 }
548}
549
550/// Provides advanced methods to select or insert an entry of the cache.
551///
552/// Many methods here return an [`Entry`], a snapshot of a single key-value pair in
553/// the cache, carrying additional information like `is_fresh`.
554///
555/// `RefKeyEntrySelector` is constructed from the
556/// [`entry_by_ref`][entry-by-ref-method] method on the cache.
557///
558/// [`Entry`]: ../struct.Entry.html
559/// [entry-by-ref-method]: ./struct.Cache.html#method.entry_by_ref
560pub struct RefKeyEntrySelector<'a, K, Q, V, S>
561where
562 Q: ?Sized,
563{
564 ref_key: &'a Q,
565 hash: u64,
566 cache: &'a Cache<K, V, S>,
567}
568
569impl<'a, K, Q, V, S> RefKeyEntrySelector<'a, K, Q, V, S>
570where
571 K: Hash + Eq + Send + Sync + 'static,
572 Q: Equivalent<K> + ToOwned<Owned = K> + Hash + ?Sized,
573 V: Clone + Send + Sync + 'static,
574 S: BuildHasher + Clone + Send + Sync + 'static,
575{
576 pub(crate) fn new(ref_key: &'a Q, hash: u64, cache: &'a Cache<K, V, S>) -> Self {
577 Self {
578 ref_key,
579 hash,
580 cache,
581 }
582 }
583
584 /// Performs a compute operation on a cached entry by using the given closure
585 /// `f`. A compute operation is either put, remove or no-operation (nop).
586 ///
587 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
588 /// the key, and return an `ops::compute::Op<V>` enum.
589 ///
590 /// This method works as the followings:
591 ///
592 /// 1. Apply the closure `f` to the current cached `Entry`, and get an
593 /// `ops::compute::Op<V>`.
594 /// 2. Execute the op on the cache:
595 /// - `Op::Put(V)`: Put the new value `V` to the cache.
596 /// - `Op::Remove`: Remove the current cached entry.
597 /// - `Op::Nop`: Do nothing.
598 /// 3. Return an `ops::compute::CompResult<K, V>` as the followings:
599 ///
600 /// | [`Op<V>`] | [`Entry<K, V>`] already exists? | [`CompResult<K, V>`] | Notes |
601 /// |:--------- |:--- |:--------------------------- |:------------------------------- |
602 /// | `Put(V)` | no | `Inserted(Entry<K, V>)` | The new entry is returned. |
603 /// | `Put(V)` | yes | `ReplacedWith(Entry<K, V>)` | The new entry is returned. |
604 /// | `Remove` | no | `StillNone(Arc<K>)` | |
605 /// | `Remove` | yes | `Removed(Entry<K, V>)` | The removed entry is returned. |
606 /// | `Nop` | no | `StillNone(Arc<K>)` | |
607 /// | `Nop` | yes | `Unchanged(Entry<K, V>)` | The existing entry is returned. |
608 ///
609 /// # See Also
610 ///
611 /// - If you want the `Future` resolve to `Result<Op<V>>` instead of `Op<V>`, and
612 /// modify entry only when resolved to `Ok(V)`, use the
613 /// [`and_try_compute_with`] method.
614 /// - If you only want to update or insert, use the [`and_upsert_with`] method.
615 ///
616 /// [`Entry<K, V>`]: ../struct.Entry.html
617 /// [`Op<V>`]: ../ops/compute/enum.Op.html
618 /// [`CompResult<K, V>`]: ../ops/compute/enum.CompResult.html
619 /// [`and_upsert_with`]: #method.and_upsert_with
620 /// [`and_try_compute_with`]: #method.and_try_compute_with
621 ///
622 /// # Example
623 ///
624 /// ```rust
625 /// use moka::{
626 /// sync::Cache,
627 /// ops::compute::{CompResult, Op},
628 /// };
629 ///
630 /// let cache: Cache<String, u64> = Cache::new(100);
631 /// let key = "key1".to_string();
632 ///
633 /// /// Increment a cached `u64` counter. If the counter is greater than or
634 /// /// equal to 2, remove it.
635 /// fn inclement_or_remove_counter(
636 /// cache: &Cache<String, u64>,
637 /// key: &str,
638 /// ) -> CompResult<String, u64> {
639 /// cache
640 /// .entry_by_ref(key)
641 /// .and_compute_with(|maybe_entry| {
642 /// if let Some(entry) = maybe_entry {
643 /// let counter = entry.into_value();
644 /// if counter < 2 {
645 /// Op::Put(counter.saturating_add(1)) // Update
646 /// } else {
647 /// Op::Remove
648 /// }
649 /// } else {
650 /// Op::Put(1) // Insert
651 /// }
652 /// })
653 /// }
654 ///
655 /// // This should insert a now counter value 1 to the cache, and return the
656 /// // value with the kind of the operation performed.
657 /// let result = inclement_or_remove_counter(&cache, &key);
658 /// let CompResult::Inserted(entry) = result else {
659 /// panic!("`Inserted` should be returned: {result:?}");
660 /// };
661 /// assert_eq!(entry.into_value(), 1);
662 ///
663 /// // This should increment the cached counter value by 1.
664 /// let result = inclement_or_remove_counter(&cache, &key);
665 /// let CompResult::ReplacedWith(entry) = result else {
666 /// panic!("`ReplacedWith` should be returned: {result:?}");
667 /// };
668 /// assert_eq!(entry.into_value(), 2);
669 ///
670 /// // This should remove the cached counter from the cache, and returns the
671 /// // _removed_ value.
672 /// let result = inclement_or_remove_counter(&cache, &key);
673 /// let CompResult::Removed(entry) = result else {
674 /// panic!("`Removed` should be returned: {result:?}");
675 /// };
676 /// assert_eq!(entry.into_value(), 2);
677 ///
678 /// // The key should no longer exist.
679 /// assert!(!cache.contains_key(&key));
680 ///
681 /// // This should start over; insert a new counter value 1 to the cache.
682 /// let result = inclement_or_remove_counter(&cache, &key);
683 /// let CompResult::Inserted(entry) = result else {
684 /// panic!("`Inserted` should be returned: {result:?}");
685 /// };
686 /// assert_eq!(entry.into_value(), 1);
687 /// ```
688 ///
689 /// # Concurrent calls on the same key
690 ///
691 /// This method guarantees that concurrent calls on the same key are executed
692 /// serially. That is, `and_compute_with` calls on the same key never run
693 /// concurrently. The calls are serialized by the order of their invocation. It
694 /// uses a key-level lock to achieve this.
695 pub fn and_compute_with<F>(self, f: F) -> compute::CompResult<K, V>
696 where
697 F: FnOnce(Option<Entry<K, V>>) -> compute::Op<V>,
698 {
699 let key = Arc::new(self.ref_key.to_owned());
700 self.cache.compute_with_hash_and_fun(key, self.hash, f)
701 }
702
703 /// Performs a compute operation on a cached entry by using the given closure
704 /// `f`. A compute operation is either put, remove or no-operation (nop).
705 ///
706 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
707 /// the key, and return a `Result<ops::compute::Op<V>, E>`.
708 ///
709 /// This method works as the followings:
710 ///
711 /// 1. Apply the closure `f` to the current cached `Entry`, and get a
712 /// `Result<ops::compute::Op<V>, E>`.
713 /// 2. If resolved to `Err(E)`, return it.
714 /// 3. Else, execute the op on the cache:
715 /// - `Ok(Op::Put(V))`: Put the new value `V` to the cache.
716 /// - `Ok(Op::Remove)`: Remove the current cached entry.
717 /// - `Ok(Op::Nop)`: Do nothing.
718 /// 4. Return an `Ok(ops::compute::CompResult<K, V>)` as the followings:
719 ///
720 /// | [`Op<V>`] | [`Entry<K, V>`] already exists? | [`CompResult<K, V>`] | Notes |
721 /// |:--------- |:--- |:--------------------------- |:------------------------------- |
722 /// | `Put(V)` | no | `Inserted(Entry<K, V>)` | The new entry is returned. |
723 /// | `Put(V)` | yes | `ReplacedWith(Entry<K, V>)` | The new entry is returned. |
724 /// | `Remove` | no | `StillNone(Arc<K>)` | |
725 /// | `Remove` | yes | `Removed(Entry<K, V>)` | The removed entry is returned. |
726 /// | `Nop` | no | `StillNone(Arc<K>)` | |
727 /// | `Nop` | yes | `Unchanged(Entry<K, V>)` | The existing entry is returned. |
728 ///
729 /// # Similar Methods
730 ///
731 /// - If you want the `Future` resolve to `Op<V>` instead of `Result<Op<V>>`, use
732 /// the [`and_compute_with`] method.
733 /// - If you only want to update or insert, use the [`and_upsert_with`] method.
734 ///
735 /// [`Entry<K, V>`]: ../struct.Entry.html
736 /// [`Op<V>`]: ../ops/compute/enum.Op.html
737 /// [`CompResult<K, V>`]: ../ops/compute/enum.CompResult.html
738 /// [`and_upsert_with`]: #method.and_upsert_with
739 /// [`and_compute_with`]: #method.and_compute_with
740 ///
741 /// # Example
742 ///
743 /// See [`try_append_value_async.rs`] in the `examples` directory.
744 ///
745 /// [`try_append_value_sync.rs`]:
746 /// https://github.com/moka-rs/moka/tree/main/examples/try_append_value_sync.rs
747 ///
748 /// # Concurrent calls on the same key
749 ///
750 /// This method guarantees that concurrent calls on the same key are executed
751 /// serially. That is, `and_try_compute_with` calls on the same key never run
752 /// concurrently. The calls are serialized by the order of their invocation. It
753 /// uses a key-level lock to achieve this.
754 pub fn and_try_compute_with<F, E>(self, f: F) -> Result<compute::CompResult<K, V>, E>
755 where
756 F: FnOnce(Option<Entry<K, V>>) -> Result<compute::Op<V>, E>,
757 E: Send + Sync + 'static,
758 {
759 let key = Arc::new(self.ref_key.to_owned());
760 self.cache.try_compute_with_hash_and_fun(key, self.hash, f)
761 }
762
763 /// Performs an upsert of an [`Entry`] by using the given closure `f`. The word
764 /// "upsert" here means "update" or "insert".
765 ///
766 /// The closure `f` should take the current entry of `Option<Entry<K, V>>` for
767 /// the key, and return a new value `V`.
768 ///
769 /// This method works as the followings:
770 ///
771 /// 1. Apply the closure `f` to the current cached `Entry`, and get a new value
772 /// `V`.
773 /// 2. Upsert the new value to the cache.
774 /// 3. Return the `Entry` having the upserted value.
775 ///
776 /// # Similar Methods
777 ///
778 /// - If you want to optionally upsert, that is to upsert only when certain
779 /// conditions meet, use the [`and_compute_with`] method.
780 /// - If you try to upsert, that is to make the `Future` resolve to `Result<V>`
781 /// instead of `V`, and upsert only when resolved to `Ok(V)`, use the
782 /// [`and_try_compute_with`] method.
783 ///
784 /// [`Entry`]: ../struct.Entry.html
785 /// [`and_compute_with`]: #method.and_compute_with
786 /// [`and_try_compute_with`]: #method.and_try_compute_with
787 ///
788 /// # Example
789 ///
790 /// ```rust
791 /// use moka::sync::Cache;
792 ///
793 /// let cache: Cache<String, u64> = Cache::new(100);
794 /// let key = "key1".to_string();
795 ///
796 /// let entry = cache
797 /// .entry_by_ref(&key)
798 /// .and_upsert_with(|maybe_entry| {
799 /// if let Some(entry) = maybe_entry {
800 /// entry.into_value().saturating_add(1) // Update
801 /// } else {
802 /// 1 // Insert
803 /// }
804 /// });
805 /// // It was not an update.
806 /// assert!(!entry.is_old_value_replaced());
807 /// assert_eq!(entry.key(), &key);
808 /// assert_eq!(entry.into_value(), 1);
809 ///
810 /// let entry = cache
811 /// .entry_by_ref(&key)
812 /// .and_upsert_with(|maybe_entry| {
813 /// if let Some(entry) = maybe_entry {
814 /// entry.into_value().saturating_add(1)
815 /// } else {
816 /// 1
817 /// }
818 /// });
819 /// // It was an update.
820 /// assert!(entry.is_old_value_replaced());
821 /// assert_eq!(entry.key(), &key);
822 /// assert_eq!(entry.into_value(), 2);
823 /// ```
824 ///
825 /// # Concurrent calls on the same key
826 ///
827 /// This method guarantees that concurrent calls on the same key are executed
828 /// serially. That is, `and_upsert_with` calls on the same key never run
829 /// concurrently. The calls are serialized by the order of their invocation. It
830 /// uses a key-level lock to achieve this.
831 pub fn and_upsert_with<F>(self, f: F) -> Entry<K, V>
832 where
833 F: FnOnce(Option<Entry<K, V>>) -> V,
834 {
835 let key = Arc::new(self.ref_key.to_owned());
836 self.cache.upsert_with_hash_and_fun(key, self.hash, f)
837 }
838
839 /// Returns the corresponding [`Entry`] for the reference of the key given when
840 /// this entry selector was constructed. If the entry does not exist, inserts one
841 /// by cloning the key and calling the [`default`][std-default-function] function
842 /// of the value type `V`.
843 ///
844 /// [`Entry`]: ../struct.Entry.html
845 /// [std-default-function]: https://doc.rust-lang.org/stable/std/default/trait.Default.html#tymethod.default
846 ///
847 /// # Example
848 ///
849 /// ```rust
850 /// use moka::sync::Cache;
851 ///
852 /// let cache: Cache<String, Option<u32>> = Cache::new(100);
853 /// let key = "key1".to_string();
854 ///
855 /// let entry = cache.entry_by_ref(&key).or_default();
856 /// assert!(entry.is_fresh());
857 /// assert_eq!(entry.key(), &key);
858 /// assert_eq!(entry.into_value(), None);
859 ///
860 /// let entry = cache.entry_by_ref(&key).or_default();
861 /// // Not fresh because the value was already in the cache.
862 /// assert!(!entry.is_fresh());
863 /// ```
864 pub fn or_default(self) -> Entry<K, V>
865 where
866 V: Default,
867 {
868 self.cache
869 .get_or_insert_with_hash_by_ref(self.ref_key, self.hash, Default::default)
870 }
871
872 /// Returns the corresponding [`Entry`] for the reference of the key given when
873 /// this entry selector was constructed. If the entry does not exist, inserts one
874 /// by cloning the key and using the given `default` value for `V`.
875 ///
876 /// [`Entry`]: ../struct.Entry.html
877 ///
878 /// # Example
879 ///
880 /// ```rust
881 /// use moka::sync::Cache;
882 ///
883 /// let cache: Cache<String, u32> = Cache::new(100);
884 /// let key = "key1".to_string();
885 ///
886 /// let entry = cache.entry_by_ref(&key).or_insert(3);
887 /// assert!(entry.is_fresh());
888 /// assert_eq!(entry.key(), &key);
889 /// assert_eq!(entry.into_value(), 3);
890 ///
891 /// let entry = cache.entry_by_ref(&key).or_insert(6);
892 /// // Not fresh because the value was already in the cache.
893 /// assert!(!entry.is_fresh());
894 /// assert_eq!(entry.into_value(), 3);
895 /// ```
896 pub fn or_insert(self, default: V) -> Entry<K, V> {
897 let init = || default;
898 self.cache
899 .get_or_insert_with_hash_by_ref(self.ref_key, self.hash, init)
900 }
901
902 /// Returns the corresponding [`Entry`] for the reference of the key given when
903 /// this entry selector was constructed. If the entry does not exist, inserts one
904 /// by cloning the key and evaluating the `init` closure for the value.
905 ///
906 /// [`Entry`]: ../struct.Entry.html
907 ///
908 /// # Example
909 ///
910 /// ```rust
911 /// use moka::sync::Cache;
912 ///
913 /// let cache: Cache<String, String> = Cache::new(100);
914 /// let key = "key1".to_string();
915 ///
916 /// let entry = cache
917 /// .entry_by_ref(&key)
918 /// .or_insert_with(|| "value1".to_string());
919 /// assert!(entry.is_fresh());
920 /// assert_eq!(entry.key(), &key);
921 /// assert_eq!(entry.into_value(), "value1");
922 ///
923 /// let entry = cache
924 /// .entry_by_ref(&key)
925 /// .or_insert_with(|| "value2".to_string());
926 /// // Not fresh because the value was already in the cache.
927 /// assert!(!entry.is_fresh());
928 /// assert_eq!(entry.into_value(), "value1");
929 /// ```
930 ///
931 /// # Concurrent calls on the same key
932 ///
933 /// This method guarantees that concurrent calls on the same not-existing entry
934 /// are coalesced into one evaluation of the `init` closure. Only one of the
935 /// calls evaluates its closure (thus returned entry's `is_fresh` method returns
936 /// `true`), and other calls wait for that closure to complete (and their
937 /// `is_fresh` return `false`).
938 ///
939 /// For more detail about the coalescing behavior, see
940 /// [`Cache::get_with`][get-with-method].
941 ///
942 /// [get-with-method]: ./struct.Cache.html#method.get_with
943 pub fn or_insert_with(self, init: impl FnOnce() -> V) -> Entry<K, V> {
944 let replace_if = None as Option<fn(&V) -> bool>;
945 self.cache.get_or_insert_with_hash_by_ref_and_fun(
946 self.ref_key,
947 self.hash,
948 init,
949 replace_if,
950 true,
951 )
952 }
953
954 /// Works like [`or_insert_with`](#method.or_insert_with), but takes an additional
955 /// `replace_if` closure.
956 ///
957 /// This method will evaluate the `init` closure and insert the output to the
958 /// cache when:
959 ///
960 /// - The key does not exist.
961 /// - Or, `replace_if` closure returns `true`.
962 pub fn or_insert_with_if(
963 self,
964 init: impl FnOnce() -> V,
965 replace_if: impl FnMut(&V) -> bool,
966 ) -> Entry<K, V> {
967 self.cache.get_or_insert_with_hash_by_ref_and_fun(
968 self.ref_key,
969 self.hash,
970 init,
971 Some(replace_if),
972 true,
973 )
974 }
975
976 /// Returns the corresponding [`Entry`] for the reference of the key given when
977 /// this entry selector was constructed. If the entry does not exist, clones the
978 /// key and evaluates the `init` closure. If `Some(value)` was returned by the
979 /// closure, inserts an entry with the value . If `None` was returned, this
980 /// method does not insert an entry and returns `None`.
981 ///
982 /// [`Entry`]: ../struct.Entry.html
983 ///
984 /// # Example
985 ///
986 /// ```rust
987 /// use moka::sync::Cache;
988 ///
989 /// let cache: Cache<String, u32> = Cache::new(100);
990 /// let key = "key1".to_string();
991 ///
992 /// let none_entry = cache
993 /// .entry_by_ref(&key)
994 /// .or_optionally_insert_with(|| None);
995 /// assert!(none_entry.is_none());
996 ///
997 /// let some_entry = cache
998 /// .entry_by_ref(&key)
999 /// .or_optionally_insert_with(|| Some(3));
1000 /// assert!(some_entry.is_some());
1001 /// let entry = some_entry.unwrap();
1002 /// assert!(entry.is_fresh());
1003 /// assert_eq!(entry.key(), &key);
1004 /// assert_eq!(entry.into_value(), 3);
1005 ///
1006 /// let some_entry = cache
1007 /// .entry_by_ref(&key)
1008 /// .or_optionally_insert_with(|| Some(6));
1009 /// let entry = some_entry.unwrap();
1010 /// // Not fresh because the value was already in the cache.
1011 /// assert!(!entry.is_fresh());
1012 /// assert_eq!(entry.into_value(), 3);
1013 /// ```
1014 ///
1015 /// # Concurrent calls on the same key
1016 ///
1017 /// This method guarantees that concurrent calls on the same not-existing entry
1018 /// are coalesced into one evaluation of the `init` closure. Only one of the
1019 /// calls evaluates its closure (thus returned entry's `is_fresh` method returns
1020 /// `true`), and other calls wait for that closure to complete (and their
1021 /// `is_fresh` return `false`).
1022 ///
1023 /// For more detail about the coalescing behavior, see
1024 /// [`Cache::optionally_get_with`][opt-get-with-method].
1025 ///
1026 /// [opt-get-with-method]: ./struct.Cache.html#method.optionally_get_with
1027 pub fn or_optionally_insert_with(
1028 self,
1029 init: impl FnOnce() -> Option<V>,
1030 ) -> Option<Entry<K, V>> {
1031 self.cache
1032 .get_or_optionally_insert_with_hash_by_ref_and_fun(self.ref_key, self.hash, init, true)
1033 }
1034
1035 /// Returns the corresponding [`Entry`] for the reference of the key given when
1036 /// this entry selector was constructed. If the entry does not exist, clones the
1037 /// key and evaluates the `init` closure. If `Ok(value)` was returned from the
1038 /// closure, inserts an entry with the value. If `Err(_)` was returned, this
1039 /// method does not insert an entry and returns the `Err` wrapped by
1040 /// [`std::sync::Arc`][std-arc].
1041 ///
1042 /// [`Entry`]: ../struct.Entry.html
1043 /// [std-arc]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
1044 ///
1045 /// # Example
1046 ///
1047 /// ```rust
1048 /// use moka::sync::Cache;
1049 ///
1050 /// let cache: Cache<String, u32> = Cache::new(100);
1051 /// let key = "key1".to_string();
1052 ///
1053 /// let error_entry = cache
1054 /// .entry_by_ref(&key)
1055 /// .or_try_insert_with(|| Err("error"));
1056 /// assert!(error_entry.is_err());
1057 ///
1058 /// let ok_entry = cache
1059 /// .entry_by_ref(&key)
1060 /// .or_try_insert_with(|| Ok::<u32, &str>(3));
1061 /// assert!(ok_entry.is_ok());
1062 /// let entry = ok_entry.unwrap();
1063 /// assert!(entry.is_fresh());
1064 /// assert_eq!(entry.key(), &key);
1065 /// assert_eq!(entry.into_value(), 3);
1066 ///
1067 /// let ok_entry = cache
1068 /// .entry_by_ref(&key)
1069 /// .or_try_insert_with(|| Ok::<u32, &str>(6));
1070 /// let entry = ok_entry.unwrap();
1071 /// // Not fresh because the value was already in the cache.
1072 /// assert!(!entry.is_fresh());
1073 /// assert_eq!(entry.into_value(), 3);
1074 /// ```
1075 ///
1076 /// # Concurrent calls on the same key
1077 ///
1078 /// This method guarantees that concurrent calls on the same not-existing entry
1079 /// are coalesced into one evaluation of the `init` closure (as long as these
1080 /// closures return the same error type). Only one of the calls evaluates its
1081 /// closure (thus returned entry's `is_fresh` method returns `true`), and other
1082 /// calls wait for that closure to complete (and their `is_fresh` return
1083 /// `false`).
1084 ///
1085 /// For more detail about the coalescing behavior, see
1086 /// [`Cache::try_get_with`][try-get-with-method].
1087 ///
1088 /// [try-get-with-method]: ./struct.Cache.html#method.try_get_with
1089 pub fn or_try_insert_with<F, E>(self, init: F) -> Result<Entry<K, V>, Arc<E>>
1090 where
1091 F: FnOnce() -> Result<V, E>,
1092 E: Send + Sync + 'static,
1093 {
1094 self.cache
1095 .get_or_try_insert_with_hash_by_ref_and_fun(self.ref_key, self.hash, init, true)
1096 }
1097}