Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: Make it easy to build histories
Alexis Sellier committed 3 years ago
commit 2f42bc942cb124d8f8b05e1adb8a90a289cab9ba
parent 5ee1f42b6a04f372147a24e233471d9ddc6e0096
5 files changed +117 -4
modified radicle-cob/src/history.rs
@@ -38,7 +38,7 @@ pub enum CreateError {
}

impl History {
-
    pub(crate) fn new_from_root<Id>(
+
    pub fn new_from_root<Id>(
        id: Id,
        actor: PublicKey,
        resource: Oid,
@@ -114,14 +114,14 @@ impl History {
        pruning_fold::pruning_fold(init, items, f)
    }

-
    pub(crate) fn tips(&self) -> BTreeSet<Oid> {
+
    pub fn tips(&self) -> BTreeSet<Oid> {
        self.graph
            .tips()
            .map(|(_, entry)| (*entry.id()).into())
            .collect()
    }

-
    pub(crate) fn extend<Id>(
+
    pub fn extend<Id>(
        &mut self,
        new_id: Id,
        new_actor: PublicKey,
@@ -152,6 +152,10 @@ impl History {
            self.graph.dependency(new_id, (*tip).into());
        }
    }
+

+
    pub fn merge(&mut self, other: Self) {
+
        self.graph.merge(other.graph);
+
    }
}

fn create_dag<'a>(root: &'a EntryId, entries: &'a HashMap<EntryId, EntryWithClock>) -> History {
modified radicle-dag/src/lib.rs
@@ -130,6 +130,21 @@ impl<K: Eq + Copy + Hash, V> Dag<K, V> {
            .filter_map(|k| self.graph.get(k).map(|n| (k, n)))
    }

+
    /// Merge a DAG into this one.
+
    ///
+
    /// If a key exists in both graphs, its value is set to that of the other graph.
+
    pub fn merge(&mut self, other: Self) {
+
        for k in other.tips.into_iter() {
+
            self.tips.insert(k);
+
        }
+
        for k in other.roots.into_iter() {
+
            self.roots.insert(k);
+
        }
+
        for (k, v) in other.graph.into_iter() {
+
            self.graph.insert(k, v);
+
        }
+
    }
+

    /// Return a topological ordering of the graph's nodes, using the given RNG.
    /// Graphs with more than one partial order will return an arbitrary topological ordering.
    ///
@@ -239,6 +254,30 @@ mod tests {
    }

    #[test]
+
    fn test_merge() {
+
        let mut a = Dag::new();
+
        let mut b = Dag::new();
+
        let mut c = Dag::new();
+

+
        a.node(0, ());
+
        a.node(1, ());
+
        a.dependency(1, 0);
+

+
        b.node(0, ());
+
        b.node(2, ());
+
        b.dependency(2, 0);
+

+
        c.merge(a);
+
        c.merge(b);
+

+
        assert!(c.get(&0).is_some());
+
        assert!(c.get(&1).is_some());
+
        assert!(c.get(&2).is_some());
+
        assert!(c.has_dependency(&1, &0));
+
        assert!(c.has_dependency(&2, &0));
+
    }
+

+
    #[test]
    fn test_diamond() {
        let mut dag = Dag::new();

modified radicle/src/cob.rs
@@ -5,6 +5,9 @@ pub mod patch;
pub mod store;
pub mod thread;

+
#[cfg(test)]
+
pub mod test;
+

pub use cob::{create, get, list, remove, update};
pub use cob::{
    identity, object::collaboration::error, CollaborativeObject, Contents, Create, Entry, History,
modified radicle/src/cob/store.rs
@@ -286,7 +286,7 @@ impl<T: FromHistory> Transaction<T> {
    }
}

-
mod encoding {
+
pub mod encoding {
    use serde::Serialize;

    /// Serialize the change into a byte string.
added radicle/src/cob/test.rs
@@ -0,0 +1,67 @@
+
use std::marker::PhantomData;
+
use std::ops::Deref;
+

+
use nonempty::NonEmpty;
+
use serde::Serialize;
+

+
use crate::cob::op::Op;
+
use crate::cob::store::encoding;
+
use crate::cob::History;
+
use crate::git::Oid;
+
use crate::test::arbitrary;
+

+
/// Convenience type for building histories.
+
#[derive(Debug, Clone)]
+
pub struct HistoryBuilder<A> {
+
    history: History,
+
    witness: PhantomData<A>,
+
    resource: Oid,
+
}
+

+
impl<A: Serialize> HistoryBuilder<A> {
+
    pub fn new(op: &Op<A>) -> HistoryBuilder<A> {
+
        let entry = arbitrary::oid();
+
        let resource = arbitrary::oid();
+
        let contents = encoding::encode(&op.action).unwrap();
+

+
        Self {
+
            history: History::new_from_root(
+
                entry,
+
                op.author,
+
                resource,
+
                NonEmpty::new(contents),
+
                op.timestamp.as_secs(),
+
            ),
+
            resource,
+
            witness: PhantomData,
+
        }
+
    }
+

+
    pub fn append(&mut self, op: &Op<A>) -> &mut Self {
+
        self.history.extend(
+
            arbitrary::oid(),
+
            op.author,
+
            self.resource,
+
            NonEmpty::new(encoding::encode(&op.action).unwrap()),
+
            op.timestamp.as_secs(),
+
        );
+
        self
+
    }
+

+
    pub fn merge(&mut self, other: Self) {
+
        self.history.merge(other.history);
+
    }
+
}
+

+
impl<A> Deref for HistoryBuilder<A> {
+
    type Target = History;
+

+
    fn deref(&self) -> &Self::Target {
+
        &self.history
+
    }
+
}
+

+
/// Create a new test history.
+
pub fn history<A: Serialize>(op: &Op<A>) -> HistoryBuilder<A> {
+
    HistoryBuilder::new(op)
+
}