ironworks/utility/
option_cache.rs1use std::sync::{Arc, Mutex};
2
3pub type OptionCache<T> = Mutex<Option<Arc<T>>>;
4
5pub trait OptionCacheExt<T> {
6 fn try_get_or_insert<E>(&self, build: impl FnOnce() -> Result<T, E>) -> Result<Arc<T>, E>;
7}
8
9impl<T> OptionCacheExt<T> for OptionCache<T> {
10 fn try_get_or_insert<E>(&self, build: impl FnOnce() -> Result<T, E>) -> Result<Arc<T>, E> {
11 Ok(match &mut *self.lock().unwrap() {
12 Some(inner) => inner.clone(),
13 option @ None => option.insert(build()?.into()).clone(),
14 })
15 }
16}
17
18#[cfg(test)]
19mod test {
20 use std::convert::Infallible;
21
22 use super::{OptionCache, OptionCacheExt};
23
24 #[test]
25 fn default() {
26 let cache: OptionCache<u8> = Default::default();
27 assert_eq!(cache.into_inner().unwrap(), None)
28 }
29
30 #[test]
31 fn builds_once() {
32 let cache: OptionCache<u8> = Default::default();
33 let mut count = 0;
34 cache
35 .try_get_or_insert(|| -> Result<u8, Infallible> {
36 count += 1;
37 Ok(1)
38 })
39 .unwrap();
40 let value = cache
41 .try_get_or_insert(|| -> Result<u8, Infallible> {
42 count += 1;
43 Ok(2)
44 })
45 .unwrap();
46
47 assert_eq!(*value, 1);
48 assert_eq!(count, 1);
49 }
50
51 #[test]
52 fn build_failures() {
53 let cache: OptionCache<u8> = Default::default();
54 cache.try_get_or_insert(|| Err(())).unwrap_err();
55 let value = cache
56 .try_get_or_insert(|| -> Result<u8, Infallible> { Ok(1) })
57 .unwrap();
58 assert_eq!(*value, 1);
59 }
60}