radicle-heartwood-lfs/radicle-crdt/src/lib.rs

155 lines
3.9 KiB
Rust

#![allow(clippy::collapsible_if)]
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::type_complexity)]
pub mod clock;
pub mod gmap;
pub mod lwwmap;
pub mod lwwreg;
pub mod lwwset;
pub mod ord;
pub mod redactable;
#[cfg(any(test, feature = "test"))]
pub mod test;
////////////////////////////////////////////////////////////////////////////////
pub use clock::Lamport;
pub use gmap::GMap;
pub use lwwmap::LWWMap;
pub use lwwreg::LWWReg;
pub use lwwset::LWWSet;
pub use ord::{Max, Min};
pub use redactable::Redactable;
////////////////////////////////////////////////////////////////////////////////
use std::collections::{BTreeMap, HashMap};
use std::hash::Hash;
/// A join-semilattice.
pub trait Semilattice: Sized {
/// Merge an other semilattice into this one.
///
/// This operation should obbey the semilattice laws and should thus be idempotent,
/// associative and commutative.
fn merge(&mut self, other: Self);
/// Like [`Semilattice::merge`] but takes and returns a new semilattice.
fn join(mut self, other: Self) -> Self {
self.merge(other);
self
}
}
impl<K: Ord, V: Semilattice> Semilattice for BTreeMap<K, V> {
fn merge(&mut self, other: Self) {
use std::collections::btree_map::Entry;
for (k, v) in other {
match self.entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().merge(v);
}
Entry::Vacant(e) => {
e.insert(v);
}
}
}
}
}
impl<K: Hash + PartialEq + Eq, V: Semilattice> Semilattice for HashMap<K, V> {
fn merge(&mut self, other: Self) {
use std::collections::hash_map::Entry;
for (k, v) in other {
match self.entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().merge(v);
}
Entry::Vacant(e) => {
e.insert(v);
}
}
}
}
}
impl<T: Semilattice> Semilattice for Option<T> {
fn merge(&mut self, other: Self) {
match (self, other) {
(this @ None, other @ Some(_)) => {
*this = other;
}
(Some(ref mut a), Some(b)) => {
a.merge(b);
}
(Some(_), None) => {}
(None, None) => {}
}
}
}
impl Semilattice for () {
fn merge(&mut self, _other: Self) {}
}
impl Semilattice for bool {
fn merge(&mut self, other: Self) {
match (&self, other) {
(false, true) => *self = true,
(true, false) => *self = true,
(false, false) | (true, true) => {}
}
}
}
pub fn fold<S>(i: impl IntoIterator<Item = S>) -> S
where
S: Semilattice + Default,
{
i.into_iter().fold(S::default(), S::join)
}
#[cfg(test)]
mod tests {
use crate::{test, Max, Min, Semilattice};
use qcheck_macros::quickcheck;
#[quickcheck]
fn prop_option_laws(a: Max<u8>, b: Max<u8>, c: Max<u8>) {
test::assert_laws(&a, &b, &c);
}
#[quickcheck]
fn prop_bool_laws(a: bool, b: bool, c: bool) {
test::assert_laws(&a, &b, &c);
}
#[test]
fn test_bool() {
assert_eq!(false.join(false), false);
assert_eq!(true.join(true), true);
assert_eq!(true.join(false), true);
assert_eq!(false.join(true), true);
}
#[test]
fn test_option() {
assert_eq!(None::<()>.join(None), None);
assert_eq!(None::<()>.join(Some(())), Some(()));
assert_eq!(Some(()).join(None), Some(()));
assert_eq!(Some(()).join(Some(())), Some(()));
assert_eq!(
Some(Max::from(0)).join(Some(Max::from(1))),
Some(Max::from(1))
);
assert_eq!(
Some(Min::from(0)).join(Some(Min::from(1))),
Some(Min::from(0))
);
}
}