use crate::{fingerprint_of, Delta, Fingerprint, Mismatch};
use std::fmt;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VersionedDelta<D> {
pub from: u64,
pub to: u64,
pub base: u64,
pub result: u64,
pub delta: D,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Versioned<T> {
value: T,
version: u64,
}
impl<T> Versioned<T> {
pub fn new(value: T) -> Self {
Versioned { value, version: 0 }
}
pub fn version(&self) -> u64 {
self.version
}
pub fn get(&self) -> &T {
&self.value
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T: Fingerprint> Versioned<T> {
pub fn fingerprint(&self) -> u64 {
fingerprint_of(&self.value)
}
}
impl<T: Delta + Fingerprint + Clone> Versioned<T> {
pub fn commit(&mut self, new: T) -> Option<VersionedDelta<T::Output>> {
let base = fingerprint_of(&self.value);
let result = fingerprint_of(&new);
let old = std::mem::replace(&mut self.value, new.clone());
Delta::delta(old, new).map(|delta| {
let from = self.version;
self.version += 1;
VersionedDelta {
from,
to: self.version,
base,
result,
delta,
}
})
}
}
impl<T: Delta + Fingerprint> Versioned<T> {
pub fn apply(&mut self, delta: VersionedDelta<T::Output>) -> Result<Applied, Rejected> {
if delta.to <= self.version {
return Ok(Applied::Stale);
}
if delta.from != self.version {
return Err(Rejected::Gap {
expected: self.version,
found: delta.from,
});
}
let found = fingerprint_of(&self.value);
if found != delta.base {
return Err(Rejected::Base {
expected: delta.base,
found,
});
}
self.value
.apply_delta(delta.delta)
.map_err(Rejected::Apply)?;
let found = fingerprint_of(&self.value);
if found != delta.result {
return Err(Rejected::Result {
expected: delta.result,
found,
});
}
self.version = delta.to;
Ok(Applied::Updated)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Applied {
Updated,
Stale,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Rejected {
Gap {
expected: u64,
found: u64,
},
Base {
expected: u64,
found: u64,
},
Result {
expected: u64,
found: u64,
},
Apply(Mismatch),
}
impl fmt::Display for Rejected {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Rejected::Gap { expected, found } => write!(
f,
"missed a delta: at version {}, but this one starts from {}",
expected, found
),
Rejected::Base { expected, found } => write!(
f,
"state has diverged: delta was computed against fingerprint {:#018x}, \
but this value is {:#018x}",
expected, found
),
Rejected::Apply(mismatch) => write!(f, "{}", mismatch),
Rejected::Result { expected, found } => write!(
f,
"delta applied to the wrong result: expected fingerprint {:#018x}, \
got {:#018x}",
expected, found
),
}
}
}
impl std::error::Error for Rejected {}