Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Make `Thread` a semilattice
Alexis Sellier committed 3 years ago
commit 550ad2b55828ab85c52a146f7767f79e6ebe03ee
parent 7e8af47f59e5362c424dcd5cb9d075bd6a4a650a
3 files changed +80 -5
modified radicle-crdt/src/lib.rs
@@ -14,6 +14,9 @@ mod test;

////////////////////////////////////////////////////////////////////////////////

+
use std::collections::{BTreeMap, HashMap};
+
use std::hash::Hash;
+

/// A join-semilattice.
pub trait Semilattice: Sized {
    /// Merge an other semilattice into this one.
@@ -29,7 +32,40 @@ pub trait Semilattice: Sized {
    }
}

-
/// Reduce an iterator of semilattice values to its least upper bound.
+
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);
+
                }
+
            }
+
        }
+
    }
+
}
+

pub fn fold<S>(i: impl IntoIterator<Item = S>) -> S
where
    S: Semilattice + Default,
modified radicle-crdt/src/redactable.rs
@@ -1,6 +1,10 @@
use crate::Semilattice;

/// An object that can be either present or removed.
+
///
+
/// Nb. The merge rules are such that if two redactables with different
+
/// values present are merged; the result is redacted. This is the preserve
+
/// the semilattice laws.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Redactable<T> {
    /// When the object is present.
@@ -19,7 +23,7 @@ impl<T> From<Option<T>> for Redactable<T> {
    }
}

-
impl<T: PartialOrd> Semilattice for Redactable<T> {
+
impl<T: PartialEq> Semilattice for Redactable<T> {
    fn merge(&mut self, other: Self) {
        match (&self, other) {
            (Self::Redacted, _) => {}
@@ -27,8 +31,8 @@ impl<T: PartialOrd> Semilattice for Redactable<T> {
                *self = Self::Redacted;
            }
            (Self::Present(a), Self::Present(b)) => {
-
                if &b > a {
-
                    *self = Self::Present(b);
+
                if a != &b {
+
                    *self = Self::Redacted;
                }
            }
        }
@@ -50,4 +54,23 @@ mod test {

        test::assert_laws(&a, &b, &c);
    }
+

+
    #[test]
+
    fn test_redacted() {
+
        let a = Redactable::Present(0);
+
        let b = Redactable::Redacted;
+

+
        assert_eq!(a.join(b), Redactable::Redacted);
+
        assert_eq!(b.join(a), Redactable::Redacted);
+
        assert_eq!(a.join(a), a);
+
    }
+

+
    #[test]
+
    fn test_both_present() {
+
        let a = Redactable::Present(0);
+
        let b = Redactable::Present(1);
+

+
        assert_eq!(a.join(b), Redactable::Redacted);
+
        assert_eq!(a.join(b), b.join(a));
+
    }
}
modified radicle-crdt/src/thread.rs
@@ -12,6 +12,7 @@ use crate::clock::LClock;
use crate::lwwreg::LWWReg;
use crate::lwwset::LWWSet;
use crate::redactable::Redactable;
+
use crate::Semilattice;

/// Identifies a change.
pub type ChangeId = radicle::hash::Digest;
@@ -85,6 +86,12 @@ impl Comment {
    }
}

+
impl PartialOrd for Comment {
+
    fn partial_cmp(&self, _other: &Self) -> Option<std::cmp::Ordering> {
+
        None
+
    }
+
}
+

/// An action that can be carried out in a change.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum Action {
@@ -115,6 +122,14 @@ pub struct Thread {
    reactions: BTreeMap<ChangeId, LWWSet<(ActorId, Reaction), Timestamp>>,
}

+
impl Semilattice for Thread {
+
    fn merge(&mut self, other: Self) {
+
        self.comments.merge(other.comments);
+
        self.tags.merge(other.tags);
+
        self.reactions.merge(other.reactions);
+
    }
+
}
+

impl Deref for Thread {
    type Target = BTreeMap<ChangeId, Redactable<Comment>>;

@@ -329,7 +344,7 @@ mod tests {
    use radicle::{cob::TypeName, crypto::test::signer::MockSigner, identity::project::Identity};

    use super::*;
-
    use crate::test::WeightedGenerator;
+
    use crate::test::{assert_laws, WeightedGenerator};

    #[derive(Clone)]
    struct Changes<const N: usize> {
@@ -574,6 +589,7 @@ mod tests {

        assert_eq!(t1, t2);
        assert_eq!(t2, t3);
+
        assert_laws(&t1, &t2, &t3);
    }

    #[test]