use crate::crdt::causal::VClock;
use crate::crdt::delta::DeltaCrdt;
use crate::crdt::replica::{generate_replica_id, ReplicaId};
use crate::crdt::Merge;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct YATADelta<T> {
pub items: Vec<YataItem<T>>,
pub clock: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct YataId {
pub clock: u64,
pub replica: ReplicaId,
}
impl YataId {
fn new(clock: u64, replica: ReplicaId) -> Self {
Self { clock, replica }
}
}
impl Ord for YataId {
fn cmp(&self, other: &Self) -> Ordering {
match self.clock.cmp(&other.clock) {
Ordering::Equal => self.replica.cmp(&other.replica),
ord => ord,
}
}
}
impl PartialOrd for YataId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct YataItem<T> {
pub id: YataId,
pub value: T,
pub deleted: bool,
pub origin_left: Option<YataId>,
pub origin_right: Option<YataId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct YATA<T> {
items: Vec<YataItem<T>>,
clock: u64,
replica_id: ReplicaId,
}
impl<T: Clone + PartialEq> YATA<T> {
pub fn new(replica_id: ReplicaId) -> Self {
Self {
items: Vec::new(),
clock: 0,
replica_id,
}
}
pub fn new_random() -> Self {
Self::new(generate_replica_id())
}
pub fn append(&mut self, value: T) {
self.clock += 1;
let id = YataId::new(self.clock, self.replica_id);
let origin_left = self.last_visible_id();
self.items.push(YataItem {
id,
value,
deleted: false,
origin_left,
origin_right: None,
});
}
pub fn insert_after(&mut self, index: usize, value: T) {
let origin_left = self.visible_id_at(index);
let origin_right = self.visible_id_at(index + 1);
self.clock += 1;
let id = YataId::new(self.clock, self.replica_id);
self.items.push(YataItem {
id,
value,
deleted: false,
origin_left,
origin_right,
});
}
pub fn insert_before(&mut self, index: usize, value: T) {
if index == 0 {
self.clock += 1;
let id = YataId::new(self.clock, self.replica_id);
let origin_right = self.visible_id_at(0);
self.items.push(YataItem {
id,
value,
deleted: false,
origin_left: None,
origin_right,
});
} else {
self.insert_after(index - 1, value);
}
}
pub fn remove(&mut self, index: usize) {
if let Some(id) = self.visible_id_at(index) {
if let Some(item) = self.items.iter_mut().find(|i| i.id == id) {
item.deleted = true;
}
}
}
pub fn get(&self, index: usize) -> Option<&T> {
self.visible_items().nth(index).map(|i| &i.value)
}
pub fn len(&self) -> usize {
self.visible_items().count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn to_vec(&self) -> Vec<T> {
self.visible_items().map(|i| i.value.clone()).collect()
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.visible_items().map(|i| &i.value)
}
fn visible_items(&self) -> impl Iterator<Item = &YataItem<T>> {
self.sorted_items().into_iter().filter(|i| !i.deleted)
}
fn sorted_items(&self) -> Vec<&YataItem<T>> {
let mut result: Vec<&YataItem<T>> = Vec::new();
let mut to_process: Vec<&YataItem<T>> = self
.items
.iter()
.filter(|i| i.origin_left.is_none())
.collect();
to_process.sort_by(|a, b| a.id.cmp(&b.id));
while let Some(item) = to_process.pop() {
result.push(item);
let mut followers: Vec<&YataItem<T>> = self
.items
.iter()
.filter(|i| i.origin_left == Some(item.id))
.collect();
followers.sort_by(|a, b| a.id.cmp(&b.id));
to_process.extend(followers);
}
result
}
fn last_visible_id(&self) -> Option<YataId> {
self.sorted_items()
.into_iter()
.filter(|i| !i.deleted)
.last()
.map(|i| i.id)
}
fn visible_id_at(&self, index: usize) -> Option<YataId> {
self.visible_items().nth(index).map(|i| i.id)
}
}
impl<T: Clone + PartialEq> Merge for YATA<T> {
fn merge(&mut self, other: &Self) {
self.clock = self.clock.max(other.clock);
for other_item in &other.items {
let exists = self.items.iter().any(|i| i.id == other_item.id);
if !exists {
self.items.push(other_item.clone());
} else if let Some(my_item) = self.items.iter_mut().find(|i| i.id == other_item.id) {
if other_item.deleted {
my_item.deleted = true;
}
}
}
}
}
impl<T: Clone + PartialEq + Serialize + DeserializeOwned + Send + 'static> DeltaCrdt for YATA<T> {
type Delta = YATADelta<T>;
fn delta_since(&self, since: &VClock) -> Option<Self::Delta> {
let current = self.version();
if since.dominates(¤t) {
return None;
}
Some(YATADelta {
items: self.items.clone(),
clock: self.clock,
})
}
fn apply_delta(&mut self, delta: &Self::Delta) {
self.clock = self.clock.max(delta.clock);
for delta_item in &delta.items {
let exists = self.items.iter().any(|i| i.id == delta_item.id);
if !exists {
self.items.push(delta_item.clone());
} else if let Some(my_item) = self.items.iter_mut().find(|i| i.id == delta_item.id) {
if delta_item.deleted {
my_item.deleted = true;
}
}
}
}
fn version(&self) -> VClock {
let mut clock = VClock::new();
for item in &self.items {
let current = clock.get(item.id.replica);
if item.id.clock > current {
for _ in current..item.id.clock {
clock.increment(item.id.replica);
}
}
}
clock
}
}
impl<T: Clone + PartialEq> Default for YATA<T> {
fn default() -> Self {
Self::new_random()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_yata_append() {
let mut seq: YATA<char> = YATA::new(1);
seq.append('a');
seq.append('b');
assert_eq!(seq.to_vec(), vec!['a', 'b']);
}
#[test]
fn test_yata_concurrent() {
let mut a: YATA<char> = YATA::new(1);
let mut b: YATA<char> = YATA::new(2);
a.append('A');
b.append('B');
a.merge(&b);
b.merge(&a);
assert_eq!(a.to_vec(), b.to_vec());
}
}