use crate::{reconcile::CounterReconciler, Hydrate, Reconcile};
#[derive(Clone)]
pub struct Counter(State);
impl std::fmt::Debug for Counter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Counter")
.field("value", &self.value())
.finish()
}
}
impl std::default::Default for Counter {
fn default() -> Self {
Self::with_value(0)
}
}
#[derive(Clone)]
enum State {
Fresh(i64),
Rehydrated { original: i64, increment: i64 },
}
impl Counter {
pub fn with_value(value: i64) -> Self {
Self(State::Fresh(value))
}
pub fn increment(&mut self, by: i64) {
match &mut self.0 {
State::Fresh(v) => *v += by,
State::Rehydrated { increment, .. } => *increment += by,
}
}
pub fn value(&self) -> i64 {
match self.0 {
State::Fresh(v) => v,
State::Rehydrated {
original,
increment,
} => original + increment,
}
}
}
impl Reconcile for Counter {
type Key<'a> = crate::reconcile::NoKey;
fn reconcile<R: crate::Reconciler>(&self, mut reconciler: R) -> Result<(), R::Error> {
let mut c = reconciler.counter()?;
match self.0 {
State::Fresh(v) => c.set(v)?,
State::Rehydrated { increment, .. } => c.increment(increment)?,
};
Ok(())
}
}
impl Hydrate for Counter {
fn hydrate_counter(c: i64) -> Result<Self, crate::HydrateError> {
Ok(Counter(State::Rehydrated {
original: c,
increment: 0,
}))
}
}
#[cfg(test)]
mod tests {
use automerge::ActorId;
use super::Counter;
use crate::{hydrate_prop, reconcile_prop};
#[test]
fn simple_increment() {
let mut doc = automerge::AutoCommit::new();
let counter = Counter::default();
reconcile_prop(&mut doc, automerge::ROOT, "counter", &counter).unwrap();
let mut counter2: Counter = hydrate_prop(&doc, &automerge::ROOT, "counter").unwrap();
let mut doc2 = doc.fork().with_actor(ActorId::random());
counter2.increment(5);
reconcile_prop(&mut doc2, automerge::ROOT, "counter", &counter2).unwrap();
let mut counter3: Counter = hydrate_prop(&doc, &automerge::ROOT, "counter").unwrap();
counter3.increment(3);
reconcile_prop(&mut doc, automerge::ROOT, "counter", &counter3).unwrap();
doc.merge(&mut doc2).unwrap();
let counter: Counter = hydrate_prop(&doc, &automerge::ROOT, "counter").unwrap();
assert_eq!(counter.value(), 8);
}
}