use std::cell::RefCell;
use std::collections::BTreeMap;
use crate::Context;
use crate::cell::Source;
pub trait Ephemeral {}
pub trait Durable {}
pub struct EphemeralValue<T>(pub T);
impl<T> Ephemeral for EphemeralValue<T> {}
#[derive(Debug, Clone)]
pub struct EphemeralCore<T> {
value: Option<T>,
expiry: u64,
}
impl<T: Clone> Default for EphemeralCore<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone> EphemeralCore<T> {
pub fn new() -> Self {
Self {
value: None,
expiry: 0,
}
}
pub fn set(&mut self, value: T, now: u64, ttl: u64) {
self.value = Some(value);
self.expiry = now + ttl;
}
pub fn tick(&mut self, now: u64) {
if self.value.is_some() && now >= self.expiry {
self.value = None;
}
}
pub fn value(&self) -> Option<T> {
self.value.clone()
}
}
impl<T> Ephemeral for EphemeralCore<T> {}
pub struct EphemeralCell<T> {
core: RefCell<EphemeralCore<T>>,
value: Source<Option<T>>,
}
impl<T: Clone + PartialEq + 'static> EphemeralCell<T> {
pub fn new(ctx: &Context) -> Self {
Self {
core: RefCell::new(EphemeralCore::new()),
value: ctx.cell(None),
}
}
fn refresh(&self, ctx: &Context) {
let v = self.core.borrow().value();
self.value.set(ctx, v);
}
pub fn set(&self, ctx: &Context, value: T, now: u64, ttl: u64) {
self.core.borrow_mut().set(value, now, ttl);
self.refresh(ctx);
}
pub fn tick(&self, ctx: &Context, now: u64) {
self.core.borrow_mut().tick(now);
self.refresh(ctx);
}
pub fn value(&self, ctx: &Context) -> Option<T> {
self.value.get(ctx)
}
pub fn value_cell(&self) -> Source<Option<T>> {
self.value
}
}
impl<T> Ephemeral for EphemeralCell<T> {}
#[derive(Debug, Clone)]
pub struct EphemeralMapCore<K, V> {
entries: BTreeMap<K, (V, u64)>,
}
impl<K: Ord + Clone, V: Clone> Default for EphemeralMapCore<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Ord + Clone, V: Clone> EphemeralMapCore<K, V> {
pub fn new() -> Self {
Self {
entries: BTreeMap::new(),
}
}
pub fn set(&mut self, key: K, value: V, now: u64, ttl: u64) {
self.entries.insert(key, (value, now + ttl));
}
pub fn evict(&mut self, key: &K) {
self.entries.remove(key);
}
pub fn tick(&mut self, now: u64) {
self.entries.retain(|_, (_, expiry)| now < *expiry);
}
pub fn get(&self, key: &K, now: u64) -> Option<V> {
self.entries
.get(key)
.filter(|(_, expiry)| now < *expiry)
.map(|(v, _)| v.clone())
}
pub fn present(&self, now: u64) -> BTreeMap<K, V> {
self.entries
.iter()
.filter(|(_, (_, expiry))| now < *expiry)
.map(|(k, (v, _))| (k.clone(), v.clone()))
.collect()
}
}
impl<K, V> Ephemeral for EphemeralMapCore<K, V> {}
pub struct PresenceCell<K, V> {
core: RefCell<EphemeralMapCore<K, V>>,
present: Source<BTreeMap<K, V>>,
ttl: u64,
}
impl<K: Ord + Clone + 'static, V: Clone + PartialEq + 'static> PresenceCell<K, V> {
pub fn new(ctx: &Context, ttl: u64) -> Self {
Self {
core: RefCell::new(EphemeralMapCore::new()),
present: ctx.cell(BTreeMap::new()),
ttl,
}
}
fn refresh(&self, ctx: &Context, now: u64) {
let p = self.core.borrow().present(now);
self.present.set(ctx, p);
}
pub fn heartbeat(&self, ctx: &Context, peer: K, value: V, now: u64) {
self.core.borrow_mut().set(peer, value, now, self.ttl);
self.refresh(ctx, now);
}
pub fn evict(&self, ctx: &Context, peer: &K, now: u64) {
self.core.borrow_mut().evict(peer);
self.refresh(ctx, now);
}
pub fn tick(&self, ctx: &Context, now: u64) {
self.core.borrow_mut().tick(now);
self.refresh(ctx, now);
}
pub fn present(&self, ctx: &Context) -> BTreeMap<K, V> {
self.present.get(ctx)
}
pub fn present_cell(&self) -> Source<BTreeMap<K, V>> {
self.present
}
}
impl<K, V> Ephemeral for PresenceCell<K, V> {}
pub struct AwarenessCell<K, V> {
core: RefCell<EphemeralMapCore<K, V>>,
present: Source<BTreeMap<K, V>>,
ttl: u64,
}
impl<K: Ord + Clone + 'static, V: Clone + PartialEq + 'static> AwarenessCell<K, V> {
pub fn new(ctx: &Context, ttl: u64) -> Self {
Self {
core: RefCell::new(EphemeralMapCore::new()),
present: ctx.cell(BTreeMap::new()),
ttl,
}
}
fn refresh(&self, ctx: &Context, now: u64) {
let p = self.core.borrow().present(now);
self.present.set(ctx, p);
}
pub fn set(&self, ctx: &Context, peer: K, value: V, now: u64) {
self.core.borrow_mut().set(peer, value, now, self.ttl);
self.refresh(ctx, now);
}
pub fn tick(&self, ctx: &Context, now: u64) {
self.core.borrow_mut().tick(now);
self.refresh(ctx, now);
}
pub fn get(&self, peer: &K, now: u64) -> Option<V> {
self.core.borrow().get(peer, now)
}
pub fn present(&self, ctx: &Context) -> BTreeMap<K, V> {
self.present.get(ctx)
}
pub fn present_cell(&self) -> Source<BTreeMap<K, V>> {
self.present
}
}
impl<K, V> Ephemeral for AwarenessCell<K, V> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ephemeral_expires_and_overwrites() {
let mut e = EphemeralCore::new();
e.set("a", 0, 5);
e.tick(3);
assert_eq!(e.value(), Some("a"));
e.tick(5);
assert_eq!(e.value(), None);
e.set("b", 6, 5);
e.set("c", 10, 5); assert_eq!(e.value(), Some("c"));
}
#[test]
fn presence_evict_and_ttl() {
let mut m = EphemeralMapCore::<u64, &str>::new();
m.set(1, "online", 0, 5);
m.set(2, "online", 1, 5);
m.evict(&2);
assert_eq!(m.present(2).len(), 1);
m.tick(6); assert!(m.present(6).is_empty());
}
#[test]
fn awareness_last_writer() {
let mut m = EphemeralMapCore::<u64, &str>::new();
m.set(1, "cursor-a", 0, 5);
m.set(1, "cursor-a2", 2, 5);
assert_eq!(m.get(&1, 2), Some("cursor-a2"));
}
}