ironworks 0.4.1

Modular FFXIV data toolkit written in rust.
Documentation
use std::sync::{Arc, Mutex};

pub type OptionCache<T> = Mutex<Option<Arc<T>>>;

pub trait OptionCacheExt<T> {
	fn try_get_or_insert<E>(&self, build: impl FnOnce() -> Result<T, E>) -> Result<Arc<T>, E>;
}

impl<T> OptionCacheExt<T> for OptionCache<T> {
	fn try_get_or_insert<E>(&self, build: impl FnOnce() -> Result<T, E>) -> Result<Arc<T>, E> {
		Ok(match &mut *self.lock().unwrap() {
			Some(inner) => inner.clone(),
			option @ None => option.insert(build()?.into()).clone(),
		})
	}
}

#[cfg(test)]
mod test {
	use std::convert::Infallible;

	use super::{OptionCache, OptionCacheExt};

	#[test]
	fn default() {
		let cache: OptionCache<u8> = Default::default();
		assert_eq!(cache.into_inner().unwrap(), None)
	}

	#[test]
	fn builds_once() {
		let cache: OptionCache<u8> = Default::default();
		let mut count = 0;
		cache
			.try_get_or_insert(|| -> Result<u8, Infallible> {
				count += 1;
				Ok(1)
			})
			.unwrap();
		let value = cache
			.try_get_or_insert(|| -> Result<u8, Infallible> {
				count += 1;
				Ok(2)
			})
			.unwrap();

		assert_eq!(*value, 1);
		assert_eq!(count, 1);
	}

	#[test]
	fn build_failures() {
		let cache: OptionCache<u8> = Default::default();
		cache.try_get_or_insert(|| Err(())).unwrap_err();
		let value = cache
			.try_get_or_insert(|| -> Result<u8, Infallible> { Ok(1) })
			.unwrap();
		assert_eq!(*value, 1);
	}
}