use std::cell::RefCell;
use std::collections::BTreeSet;
use std::marker::PhantomData;
use std::ops::Add;
use crate::Context;
use crate::cell::CellHandle;
#[cfg(feature = "distributed")]
use crate::crdt::CellCrdt;
use crate::effect::EffectHandle;
use crate::signal::SignalHandle;
use crate::slot::SlotHandle;
pub trait MergePolicy<T> {
fn merge(old: &T, op: T) -> T;
const COMMUTATIVE: bool;
const IDEMPOTENT: bool;
const CONFLATES: bool = true;
}
pub struct KeepLatest;
impl<T> MergePolicy<T> for KeepLatest {
#[inline]
fn merge(_old: &T, op: T) -> T {
op
}
const COMMUTATIVE: bool = false;
const IDEMPOTENT: bool = true;
}
pub struct Sum;
impl<T> MergePolicy<T> for Sum
where
T: Add<Output = T> + Clone,
{
#[inline]
fn merge(old: &T, op: T) -> T {
old.clone() + op
}
const COMMUTATIVE: bool = true;
const IDEMPOTENT: bool = false;
}
pub struct Max;
impl<T> MergePolicy<T> for Max
where
T: Ord + Clone,
{
#[inline]
fn merge(old: &T, op: T) -> T {
if op > *old { op } else { old.clone() }
}
const COMMUTATIVE: bool = true;
const IDEMPOTENT: bool = true;
}
pub struct SetUnion;
impl<E> MergePolicy<BTreeSet<E>> for SetUnion
where
E: Ord + Clone,
{
#[inline]
fn merge(old: &BTreeSet<E>, op: BTreeSet<E>) -> BTreeSet<E> {
let mut out = old.clone();
out.extend(op);
out
}
const COMMUTATIVE: bool = true;
const IDEMPOTENT: bool = true;
}
pub struct RawFifo;
impl<E> MergePolicy<Vec<E>> for RawFifo
where
E: Clone,
{
#[inline]
fn merge(old: &Vec<E>, op: Vec<E>) -> Vec<E> {
let mut out = old.clone();
out.extend(op);
out
}
const COMMUTATIVE: bool = false;
const IDEMPOTENT: bool = false;
const CONFLATES: bool = false;
}
#[cfg(feature = "distributed")]
pub struct CrdtJoin<C>(PhantomData<C>);
#[cfg(feature = "distributed")]
impl<C> MergePolicy<C> for CrdtJoin<C>
where
C: CellCrdt + Clone,
{
#[inline]
fn merge(old: &C, op: C) -> C {
let mut out = old.clone();
out.merge_from(&op);
out
}
const COMMUTATIVE: bool = true;
const IDEMPOTENT: bool = true;
}
pub struct MergeCellHandle<T, M> {
pub(crate) cell: CellHandle<T>,
pub(crate) _marker: PhantomData<M>,
}
impl<T, M> MergeCellHandle<T, M> {
pub(crate) fn new(cell: CellHandle<T>) -> Self {
Self {
cell,
_marker: PhantomData,
}
}
pub fn cell(&self) -> CellHandle<T> {
self.cell
}
pub fn get(&self, ctx: &Context) -> T
where
T: Clone + 'static,
{
ctx.get_cell(&self.cell)
}
pub fn merge(&self, ctx: &Context, op: T)
where
T: PartialEq + Clone + 'static,
M: MergePolicy<T>,
{
ctx.apply_merge::<T, M>(&self.cell, op);
}
pub fn set(&self, ctx: &Context, value: T)
where
T: PartialEq + 'static,
{
ctx.set_cell(&self.cell, value);
}
}
impl<T, M> Clone for MergeCellHandle<T, M> {
fn clone(&self) -> Self {
*self
}
}
impl<T, M> Copy for MergeCellHandle<T, M> {}
impl<T, M> std::fmt::Debug for MergeCellHandle<T, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MergeCellHandle")
.field("id", &self.cell.id)
.finish()
}
}
pub trait Reactive<T: Clone + 'static> {
fn get(&self, ctx: &Context) -> T;
fn subscribe(
&self,
ctx: &Context,
on_change: impl FnMut(&Context, &T) + 'static,
) -> EffectHandle
where
Self: Copy + 'static,
{
let this = *self;
let cb = RefCell::new(on_change);
ctx.effect(move |c| {
let v = this.get(c);
(cb.borrow_mut())(c, &v);
})
}
}
pub trait Source<T: Clone + 'static>: Reactive<T> {
fn set(&self, ctx: &Context, value: T);
fn merge(&self, ctx: &Context, op: T);
}
impl<T: Clone + 'static> Reactive<T> for CellHandle<T> {
fn get(&self, ctx: &Context) -> T {
ctx.get_cell(self)
}
}
impl<T: PartialEq + Clone + 'static> Source<T> for CellHandle<T> {
fn set(&self, ctx: &Context, value: T) {
ctx.set_cell(self, value);
}
fn merge(&self, ctx: &Context, op: T) {
ctx.set_cell(self, op);
}
}
impl<T: Clone + 'static> Reactive<T> for SlotHandle<T> {
fn get(&self, ctx: &Context) -> T {
ctx.get(self)
}
}
impl<T: Clone + 'static> Reactive<T> for SignalHandle<T> {
fn get(&self, ctx: &Context) -> T {
ctx.get_signal(self)
}
}
impl<T: Clone + 'static, M> Reactive<T> for MergeCellHandle<T, M> {
fn get(&self, ctx: &Context) -> T {
ctx.get_cell(&self.cell)
}
}
impl<T: PartialEq + Clone + 'static, M: MergePolicy<T>> Source<T> for MergeCellHandle<T, M> {
fn set(&self, ctx: &Context, value: T) {
ctx.set_cell(&self.cell, value);
}
fn merge(&self, ctx: &Context, op: T) {
ctx.apply_merge::<T, M>(&self.cell, op);
}
}