1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Accumulate reactive variables into a reactive total.
//!
//! The example below shows how to sum up `first_digit` and `second_digit` into
//! `total`. When we drop `first_digit`, it is removed from the total.
//!
//!```
//! # use silkenweb_reactive::{signal::SignalReceiver, accumulators::{SumTotal, SumElement}};
//! # use std::mem;
//! let total = SumTotal::<usize>::default();
//! let first_digit = SumElement::new(&total);
//! let second_digit = SumElement::new(&total);
//! let total = total.read();
//!
//! first_digit.receive(&1);
//! assert_eq!(1, *total.current());
//! second_digit.receive(&10);
//! assert_eq!(11, *total.current());
//!
//! mem::drop(first_digit);
//! assert_eq!(10, *total.current());
//! ```
use std::cell::RefCell;

use num_traits::{WrappingAdd, WrappingSub, Zero};

use crate::signal::{ReadSignal, Signal, SignalReceiver, WriteSignal};

/// This holds the current total of a sum.
///
/// See module level documentation for an example of usage.
#[derive(Clone)]
pub struct SumTotal<T> {
    deltas: WriteSignal<T>,
    total: ReadSignal<T>,
}

impl<T: 'static + Zero + Clone + WrappingAdd> Default for SumTotal<T> {
    fn default() -> Self {
        let deltas = Signal::new(T::zero());
        let total = deltas.read().map_to(AccumulateSum(RefCell::new(T::zero())));
        Self {
            deltas: deltas.write(),
            total,
        }
    }
}

impl<T: 'static> SumTotal<T> {
    pub fn read(&self) -> ReadSignal<T> {
        self.total.clone()
    }
}

/// A single element of the sum.
///
/// See module level documentation for an example of usage.
pub struct SumElement<T: 'static + Clone + Zero + WrappingAdd + WrappingSub> {
    current: RefCell<T>,
    total: SumTotal<T>,
}

impl<T: 'static + Zero + Clone + Zero + WrappingAdd + WrappingSub> SumElement<T> {
    pub fn new(total: &SumTotal<T>) -> Self {
        Self {
            current: RefCell::new(T::zero()),
            total: total.clone(),
        }
    }
}

impl<T: 'static + Clone + Zero + WrappingAdd + WrappingSub> SignalReceiver<T, SumHandle>
    for SumElement<T>
{
    fn receive(&self, x: &T) -> SumHandle {
        let delta = x.wrapping_sub(&self.current.borrow());
        self.current.replace(x.clone());
        self.total.deltas.set(delta);
        SumHandle()
    }
}

impl<T: 'static + Clone + Zero + WrappingAdd + WrappingSub> Drop for SumElement<T> {
    fn drop(&mut self) {
        self.total
            .deltas
            .set(T::zero().wrapping_sub(&self.current.borrow()));
    }
}

/// A handle to keep a value in the sum.
///
/// See module level documentation for an example of usage.
pub struct SumHandle();

struct AccumulateSum<T>(RefCell<T>);

impl<T: 'static + Clone + WrappingAdd> SignalReceiver<T, T> for AccumulateSum<T> {
    fn receive(&self, x: &T) -> T {
        let mut total = self.0.borrow_mut();
        *total = total.wrapping_add(x);
        total.clone()
    }
}