moka/sync/
entry_selector.rs

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