cob: Make it easy to build histories
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
5ee1f42b6a
commit
2f42bc942c
|
|
@ -38,7 +38,7 @@ pub enum CreateError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl History {
|
impl History {
|
||||||
pub(crate) fn new_from_root<Id>(
|
pub fn new_from_root<Id>(
|
||||||
id: Id,
|
id: Id,
|
||||||
actor: PublicKey,
|
actor: PublicKey,
|
||||||
resource: Oid,
|
resource: Oid,
|
||||||
|
|
@ -114,14 +114,14 @@ impl History {
|
||||||
pruning_fold::pruning_fold(init, items, f)
|
pruning_fold::pruning_fold(init, items, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn tips(&self) -> BTreeSet<Oid> {
|
pub fn tips(&self) -> BTreeSet<Oid> {
|
||||||
self.graph
|
self.graph
|
||||||
.tips()
|
.tips()
|
||||||
.map(|(_, entry)| (*entry.id()).into())
|
.map(|(_, entry)| (*entry.id()).into())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn extend<Id>(
|
pub fn extend<Id>(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_id: Id,
|
new_id: Id,
|
||||||
new_actor: PublicKey,
|
new_actor: PublicKey,
|
||||||
|
|
@ -152,6 +152,10 @@ impl History {
|
||||||
self.graph.dependency(new_id, (*tip).into());
|
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 {
|
fn create_dag<'a>(root: &'a EntryId, entries: &'a HashMap<EntryId, EntryWithClock>) -> History {
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,21 @@ impl<K: Eq + Copy + Hash, V> Dag<K, V> {
|
||||||
.filter_map(|k| self.graph.get(k).map(|n| (k, n)))
|
.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.
|
/// 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.
|
/// Graphs with more than one partial order will return an arbitrary topological ordering.
|
||||||
///
|
///
|
||||||
|
|
@ -238,6 +253,30 @@ mod tests {
|
||||||
assert!(expected.contains(&sorted.as_slice()));
|
assert!(expected.contains(&sorted.as_slice()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn test_diamond() {
|
fn test_diamond() {
|
||||||
let mut dag = Dag::new();
|
let mut dag = Dag::new();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ pub mod patch;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test;
|
||||||
|
|
||||||
pub use cob::{create, get, list, remove, update};
|
pub use cob::{create, get, list, remove, update};
|
||||||
pub use cob::{
|
pub use cob::{
|
||||||
identity, object::collaboration::error, CollaborativeObject, Contents, Create, Entry, History,
|
identity, object::collaboration::error, CollaborativeObject, Contents, Create, Entry, History,
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ impl<T: FromHistory> Transaction<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod encoding {
|
pub mod encoding {
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
/// Serialize the change into a byte string.
|
/// Serialize the change into a byte string.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue