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}