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}