ironworks/utility/
option_cache.rs

1use 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}