use super::common::{PositionId, AdvancedCrdtError};
use super::super::{CRDT, Mergeable, ReplicaId};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LseqElement<T> {
pub position: PositionId,
pub value: T,
pub visible: bool,
}
impl<T> LseqElement<T> {
pub fn new(position: PositionId, value: T) -> Self {
Self {
position,
value,
visible: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Lseq<T> {
replica_id: ReplicaId,
elements: BTreeMap<PositionId, LseqElement<T>>,
timestamp_counter: u64,
disambiguation_counter: u64,
}
impl<T: Clone + PartialEq> Lseq<T> {
pub fn new(replica_id: ReplicaId) -> Self {
Self {
replica_id,
elements: BTreeMap::new(),
timestamp_counter: 0,
disambiguation_counter: 0,
}
}
pub fn insert(&mut self, value: T, _position: Option<PositionId>) -> Result<PositionId, AdvancedCrdtError> {
self.timestamp_counter += 1;
self.disambiguation_counter += 1;
let new_position = PositionId::new(
self.replica_id.clone(),
self.timestamp_counter,
self.disambiguation_counter,
);
let element = LseqElement::new(new_position.clone(), value);
self.elements.insert(new_position.clone(), element);
Ok(new_position)
}
pub fn delete(&mut self, position: &PositionId) -> Result<(), AdvancedCrdtError> {
if let Some(element) = self.elements.get_mut(position) {
element.visible = false;
Ok(())
} else {
Err(AdvancedCrdtError::ElementNotFound(format!("Position {:?}", position)))
}
}
pub fn to_vec(&self) -> Vec<T> {
self.elements.values()
.filter(|e| e.visible)
.map(|e| e.value.clone())
.collect()
}
pub fn len(&self) -> usize {
self.elements.len()
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
pub fn get_elements(&self) -> &BTreeMap<PositionId, LseqElement<T>> {
&self.elements
}
}
impl<T: Clone + PartialEq> CRDT for Lseq<T> {
fn replica_id(&self) -> &ReplicaId {
&self.replica_id
}
}
impl<T: Clone + PartialEq + Send + Sync> Mergeable for Lseq<T> {
type Error = AdvancedCrdtError;
fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
for (position, other_element) in &other.elements {
if let Some(self_element) = self.elements.get_mut(position) {
if other_element.position.timestamp > self_element.position.timestamp {
*self_element = other_element.clone();
}
} else {
self.elements.insert(position.clone(), other_element.clone());
}
}
Ok(())
}
fn has_conflict(&self, other: &Self) -> bool {
for (position, self_element) in &self.elements {
if let Some(other_element) = other.elements.get(position) {
if self_element.value != other_element.value {
return true;
}
}
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::super::ReplicaId;
use uuid::Uuid;
fn create_replica(id: u64) -> ReplicaId {
ReplicaId::from(Uuid::from_u64_pair(0, id))
}
#[test]
fn test_lseq_creation() {
let replica_id = create_replica(1);
let lseq = Lseq::<String>::new(replica_id.clone());
assert_eq!(lseq.replica_id(), &replica_id);
assert!(lseq.is_empty());
assert_eq!(lseq.len(), 0);
}
#[test]
fn test_lseq_insert_and_delete() {
let replica_id = create_replica(1);
let mut lseq = Lseq::<String>::new(replica_id);
let pos1 = lseq.insert("hello".to_string(), None).unwrap();
let pos2 = lseq.insert("world".to_string(), None).unwrap();
let pos3 = lseq.insert("!".to_string(), None).unwrap();
assert_eq!(lseq.len(), 3);
let elements = lseq.to_vec();
assert!(elements.contains(&"hello".to_string()));
assert!(elements.contains(&"world".to_string()));
assert!(elements.contains(&"!".to_string()));
lseq.delete(&pos2).unwrap();
let elements = lseq.to_vec();
assert!(elements.contains(&"hello".to_string()));
assert!(!elements.contains(&"world".to_string()));
assert!(elements.contains(&"!".to_string()));
}
#[test]
fn test_lseq_merge() {
let replica_id1 = create_replica(1);
let replica_id2 = create_replica(2);
let mut lseq1 = Lseq::<String>::new(replica_id1);
let mut lseq2 = Lseq::<String>::new(replica_id2);
lseq1.insert("hello".to_string(), None).unwrap();
lseq2.insert("world".to_string(), None).unwrap();
lseq1.merge(&lseq2).unwrap();
let elements = lseq1.to_vec();
assert!(elements.contains(&"hello".to_string()));
assert!(elements.contains(&"world".to_string()));
}
}