use alloc::sync::Arc;
use arc_swap::ArcSwap;
pub struct Live<T> {
cell: Arc<ArcSwap<T>>,
}
pub struct LiveControl<T> {
cell: Arc<ArcSwap<T>>,
}
#[must_use]
pub fn live<T>(initial: T) -> (Live<T>, LiveControl<T>) {
let cell = Arc::new(ArcSwap::from_pointee(initial));
(
Live {
cell: Arc::clone(&cell),
},
LiveControl { cell },
)
}
impl<T> Clone for Live<T> {
fn clone(&self) -> Self {
Self {
cell: Arc::clone(&self.cell),
}
}
}
impl<T> Live<T> {
pub fn read<R>(&self, with: impl FnOnce(&T) -> R) -> R {
let guard = self.cell.load();
with(&guard)
}
#[must_use]
pub fn snapshot(&self) -> Arc<T> {
self.cell.load_full()
}
}
impl<T> Clone for LiveControl<T> {
fn clone(&self) -> Self {
Self {
cell: Arc::clone(&self.cell),
}
}
}
impl<T> LiveControl<T> {
pub fn replace(&self, next: T) {
self.cell.store(Arc::new(next));
}
pub fn update(&self, mutate: impl Fn(&T) -> T) {
self.cell.rcu(|current| Arc::new(mutate(current)));
}
#[must_use]
pub fn snapshot(&self) -> Arc<T> {
self.cell.load_full()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn read_sees_the_initial_value() {
let (live_value, _control) = live(41_u64);
assert_eq!(live_value.read(|value| *value), 41);
}
#[test]
fn replace_is_visible_to_an_existing_reader() {
let (live_value, control) = live(41_u64);
control.replace(108_500);
assert_eq!(live_value.read(|value| *value), 108_500);
}
#[test]
fn update_derives_next_from_current() {
let (live_value, control) = live(100_u64);
control.update(|current| current + 25);
assert_eq!(live_value.read(|value| *value), 125);
}
#[test]
fn all_clones_share_one_cell() {
let (live_value, control) = live(1_u64);
let reader_a = live_value.clone();
let reader_b = live_value.clone();
control.replace(420_000);
assert_eq!(reader_a.read(|value| *value), 420_000);
assert_eq!(reader_b.read(|value| *value), 420_000);
}
#[test]
fn snapshot_escapes_the_read_borrow() {
let (live_value, control) = live(64_800_u64);
let before = live_value.snapshot();
control.replace(64_801);
assert_eq!(*before, 64_800, "snapshot is a stable owned view");
assert_eq!(
*live_value.snapshot(),
64_801,
"a fresh snapshot sees the swap"
);
}
}