use im::Vector;
use std::cmp::Ordering;
use std::fmt::Debug;
use crate::{
time::{compare_with_tiebreak, CausalState},
CRDT,
};
#[derive(Clone)]
pub struct CausalTree<T, A> {
atom: Atom<T, A>,
children: Vector<CausalTree<T, A>>, }
#[derive(Debug)]
pub struct CausalTreeOp<T, A> {
parent_id: T,
atom: Atom<T, A>,
}
#[derive(Clone, Debug)]
struct Atom<T, A> {
id: T,
letter: Letter<A>,
}
#[derive(Clone, Debug)]
enum Letter<A> {
Letter(A),
Delete,
Root,
}
impl<T: Eq + Ord + Clone + Debug, A: Clone + Debug> CRDT for CausalTree<T, A> {
type Op = CausalTreeOp<T, A>;
type Time = T;
fn apply<CS: CausalState<Time = Self::Time>>(self, st: &CS, op: Self::Op) -> Self {
let (ct, op_ret) = insert_in_weave(st, self, op);
if op_ret.is_some() {
unreachable!("Precondition of `apply` violated: Operation must only be applied when all of its parents have been applied.")
}
ct
}
}
fn insert_in_weave<T: Eq + Ord + Clone, A: Clone, CS: CausalState<Time = T>>(
st: &CS,
weave: CausalTree<T, A>,
op: CausalTreeOp<T, A>,
) -> (CausalTree<T, A>, Option<CausalTreeOp<T, A>>) {
if weave.atom.id == op.parent_id {
let children = insert_atom(st, weave.children, op.atom);
let ct = CausalTree {
atom: weave.atom, children,
};
(ct, None)
} else {
let (children, op_ret) = insert_in_weave_children(st, weave.children, op);
let ct = CausalTree {
atom: weave.atom,
children,
};
(ct, op_ret)
}
}
fn insert_atom<T: Ord + Clone, A: Clone, CS: CausalState<Time = T>>(
st: &CS,
mut children: Vector<CausalTree<T, A>>,
atom: Atom<T, A>,
) -> Vector<CausalTree<T, A>> {
fn compare_atom<T: Ord, A, CS: CausalState<Time = T>>(
st: &CS,
a1: &Atom<T, A>,
a2: &Atom<T, A>,
) -> Ordering {
match (a1, a2) {
(
Atom {
id: id1,
letter: Letter::Root,
},
Atom {
id: id2,
letter: Letter::Root,
},
) => compare_with_tiebreak(st, id1, id2),
(
Atom { id: _, letter: _ },
Atom {
id: _,
letter: Letter::Root,
},
) => Ordering::Less,
(
Atom {
id: _,
letter: Letter::Root,
},
Atom { id: _, letter: _ },
) => Ordering::Greater,
(
Atom {
id: id1,
letter: Letter::Delete,
},
Atom {
id: id2,
letter: Letter::Delete,
},
) => compare_with_tiebreak(st, id1, id2),
(
Atom { id: _, letter: _ },
Atom {
id: _,
letter: Letter::Delete,
},
) => Ordering::Less,
(
Atom {
id: _,
letter: Letter::Delete,
},
Atom { id: _, letter: _ },
) => Ordering::Greater,
(Atom { id: id1, letter: _ }, Atom { id: id2, letter: _ }) => {
compare_with_tiebreak(st, id1, id2)
}
}
}
match children.binary_search_by(|ct| compare_atom(st, &ct.atom, &atom)) {
Err(index) => {
let ct = CausalTree {
atom,
children: Vector::new(),
};
children.insert(index, ct);
}
Ok(_index) => {
unreachable!(
"Precondition of `apply` violated: Applied `logical_time`s must be unique."
)
}
}
children
}
fn insert_in_weave_children<T: Eq + Ord + Clone, A: Clone, CS: CausalState<Time = T>>(
st: &CS,
children: Vector<CausalTree<T, A>>,
op: CausalTreeOp<T, A>,
) -> (Vector<CausalTree<T, A>>, Option<CausalTreeOp<T, A>>) {
let mut op_m = Some(op);
let children = children
.into_iter()
.map(|child| {
if let Some(op) = op_m.take() {
let (updated_child, op_ret) = insert_in_weave(st, child, op);
op_m = op_ret;
updated_child
} else {
child
}
})
.collect();
(children, op_m)
}