use super::crdt_trait::PrunableCrdt;
use super::messages::Patch;
use super::sequence_crdt::{self, SequenceElems, SequenceNode};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonCrdt {
pub id: String,
pub root: SequenceNode,
pub next_seq: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonPatch {
pub path: Vec<String>,
pub range: String,
pub content: Value,
}
impl PrunableCrdt for JsonCrdt {
fn apply_patch(&mut self, patch: Patch) {
if let Some(content_str) = patch.content.as_str() {
if patch.range.is_empty() {
self.root = SequenceNode::text(&format!("{}@{}", self.next_seq, self.id), content_str);
} else {
}
}
self.next_seq += 1;
}
fn prune(&mut self, version: &str) {
sequence_crdt::prune(&mut self.root, version);
}
fn get_next_seq(&self) -> u64 {
self.next_seq
}
fn generate_braid(
&self,
known_versions: &HashMap<String, bool>,
) -> Vec<(String, HashMap<String, bool>, Vec<Patch>)> {
let visible = |v: &str| {
if v.is_empty() {
return true;
}
!known_versions.contains_key(v)
};
vec![(
self.generate_version(),
known_versions.clone(),
vec![Patch {
range: "0".to_string(),
content: json!(sequence_crdt::content(&self.root, visible)),
}],
)]
}
}
impl JsonCrdt {
pub fn new(id: &str) -> Self {
Self {
id: id.to_string(),
root: SequenceNode {
version: None,
elems: SequenceElems::String(String::new()),
next: Vec::new(),
deleted_by: HashSet::new(),
},
next_seq: 0,
}
}
pub fn with_content(id: &str, content: &str) -> Self {
let mut crdt = Self::new(id);
crdt.root = SequenceNode::text(&format!("0@{}", id), content);
crdt.next_seq = 1;
crdt
}
pub fn get_content(&self) -> String {
sequence_crdt::content(&self.root, |_| true)
}
pub fn get_length(&self) -> usize {
sequence_crdt::length(&self.root, |_| true)
}
pub fn generate_version(&self) -> String {
format!("{}@{}", self.next_seq, self.id)
}
pub fn get_frontier(&self) -> Vec<String> {
let mut versions = HashSet::new();
let mut stack = vec![&self.root];
while let Some(node) = stack.pop() {
if let Some(v) = &node.version {
versions.insert(v.clone());
}
for child in &node.next {
stack.push(child);
}
}
versions.into_iter().collect()
}
}