use crate::patterns::lazy::types::LoadState;
use crate::utils::lock::{read_or_recover, write_or_recover};
use std::sync::{Arc, RwLock};
pub struct LazySync<T, F>
where
F: FnOnce() -> T + Send,
T: Send + Sync,
{
value: Arc<RwLock<Option<T>>>,
loader: Arc<RwLock<Option<F>>>,
state: Arc<RwLock<LoadState>>,
}
impl<T, F> LazySync<T, F>
where
F: FnOnce() -> T + Send,
T: Send + Sync,
{
pub fn new(loader: F) -> Self {
Self {
value: Arc::new(RwLock::new(None)),
loader: Arc::new(RwLock::new(Some(loader))),
state: Arc::new(RwLock::new(LoadState::Idle)),
}
}
pub fn get(&self) -> Option<T>
where
T: Clone,
{
self.ensure_loaded();
read_or_recover(&self.value).clone()
}
pub fn read(&self) -> std::sync::RwLockReadGuard<'_, Option<T>> {
self.ensure_loaded();
read_or_recover(&self.value)
}
pub fn try_read(&self) -> Option<std::sync::RwLockReadGuard<'_, Option<T>>> {
self.value.try_read().ok()
}
pub fn is_loaded(&self) -> bool {
*read_or_recover(&self.state) == LoadState::Loaded
}
pub fn state(&self) -> LoadState {
*read_or_recover(&self.state)
}
fn ensure_loaded(&self) {
{
let state = read_or_recover(&self.state);
if *state != LoadState::Idle {
return;
}
}
{
let mut state = write_or_recover(&self.state);
if *state != LoadState::Idle {
return; }
*state = LoadState::Loading;
}
if let Some(loader) = write_or_recover(&self.loader).take() {
let value = loader();
*write_or_recover(&self.value) = Some(value);
*write_or_recover(&self.state) = LoadState::Loaded;
}
}
}
impl<T, F> Clone for LazySync<T, F>
where
F: FnOnce() -> T + Send,
T: Send + Sync,
{
fn clone(&self) -> Self {
Self {
value: Arc::clone(&self.value),
loader: Arc::clone(&self.loader),
state: Arc::clone(&self.state),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::patterns::lazy::types::LoadState;
#[test]
fn test_lazy_sync_new() {
let lazy = LazySync::new(|| 42);
assert!(!lazy.is_loaded());
assert_eq!(lazy.state(), LoadState::Idle);
}
#[test]
fn test_lazy_sync_get_loads() {
let lazy = LazySync::new(|| 42);
let value = lazy.get();
assert!(value.is_some());
assert_eq!(value.unwrap(), 42);
assert!(lazy.is_loaded());
}
#[test]
fn test_lazy_sync_is_loaded() {
let lazy = LazySync::new(|| 42);
assert!(!lazy.is_loaded());
lazy.get();
assert!(lazy.is_loaded());
}
#[test]
fn test_lazy_sync_state() {
let lazy = LazySync::new(|| 42);
assert_eq!(lazy.state(), LoadState::Idle);
lazy.get();
assert_eq!(lazy.state(), LoadState::Loaded);
}
#[test]
fn test_lazy_sync_read() {
let lazy = LazySync::new(|| vec![1, 2, 3, 4, 5]);
lazy.get(); let guard = lazy.read();
assert!(guard.is_some());
assert_eq!(guard.as_ref().unwrap().len(), 5);
}
#[test]
fn test_lazy_sync_try_read() {
let lazy = LazySync::new(|| 42);
lazy.get(); let guard = lazy.try_read();
assert!(guard.is_some());
assert_eq!(guard.unwrap().as_ref().unwrap(), &42);
}
#[test]
fn test_lazy_sync_try_read_before_load() {
let lazy = LazySync::new(|| 42);
let guard = lazy.try_read();
assert!(guard.is_some());
assert!(guard.unwrap().is_none());
}
#[test]
fn test_lazy_sync_clone() {
let lazy = LazySync::new(|| 42);
lazy.get();
let cloned = lazy.clone();
assert!(cloned.is_loaded());
assert_eq!(cloned.get().unwrap(), 42);
}
#[test]
fn test_lazy_sync_get_caches() {
let lazy = LazySync::new(|| 42);
let value1 = lazy.get();
let value2 = lazy.get();
assert_eq!(value1.unwrap(), value2.unwrap());
}
#[test]
fn test_lazy_sync_with_string() {
let lazy = LazySync::new(|| "hello".to_string());
let value = lazy.get();
assert!(value.is_some());
assert_eq!(value.unwrap(), "hello");
}
}