use crate::antimatter::crdt_trait::PrunableCrdt;
use crate::antimatter::messages::Patch as AntimatterPatch;
use crate::antimatter::sequence_crdt::{self, SequenceElems, SequenceNode, Splice};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct JsonCrdt {
pub sequence: SequenceNode,
pub t: HashMap<String, HashMap<String, bool>>,
pub current_version: HashMap<String, bool>,
pub next_seq: u64,
pub id: String,
}
impl JsonCrdt {
pub fn new(id: &str) -> Self {
Self {
sequence: SequenceNode::new(
Some("root".to_string()),
SequenceElems::String(String::new()),
false,
None,
),
t: HashMap::new(),
current_version: HashMap::new(),
next_seq: 0,
id: id.to_string(),
}
}
pub fn with_content(id: &str, content: &str) -> Self {
let version = format!("0@{}", id);
let mut crdt = Self::new(id);
crdt.sequence = SequenceNode::text(&version, content);
crdt.t.insert(version.clone(), HashMap::new());
crdt.current_version.insert(version, true);
crdt.next_seq = 1;
crdt
}
pub fn generate_version(&mut self) -> String {
let version = format!("{}@{}", self.next_seq, self.id);
self.next_seq += 1;
version
}
pub fn add_version(
&mut self,
version: &str,
parents: HashMap<String, bool>,
patches: Vec<JsonPatch>,
) -> Vec<Splice> {
for p in parents.keys() {
if !self.t.contains_key(p) && p != "root" {
return Vec::new();
}
}
self.t.insert(version.to_string(), parents.clone());
for p in parents.keys() {
self.current_version.remove(p);
}
self.current_version.insert(version.to_string(), true);
let splices: Vec<Splice> = patches.iter().map(|p| p.to_splice()).collect();
let is_anc = |v: &str| self.t.contains_key(v) || v == "root";
sequence_crdt::add_version(&mut self.sequence, version, splices, is_anc)
}
pub fn get_content(&self) -> String {
let is_anc = |v: &str| self.t.contains_key(v) || v == "root";
sequence_crdt::content(&self.sequence, is_anc)
}
pub fn get_length(&self) -> usize {
let is_anc = |v: &str| self.t.contains_key(v) || v == "root";
sequence_crdt::length(&self.sequence, is_anc)
}
pub fn is_ancestor(&self, version: &str) -> bool {
self.t.contains_key(version) || version == "root"
}
pub fn get_version_graph(&self) -> &HashMap<String, HashMap<String, bool>> {
&self.t
}
pub fn get_frontier(&self) -> Vec<String> {
self.current_version.keys().cloned().collect()
}
pub fn generate_version_splices(&self, version: &str) -> Vec<Splice> {
let is_anc = |v: &str| self.t.contains_key(v) || v == "root";
sequence_crdt::generate_braid(&self.sequence, version, is_anc)
}
pub fn generate_braid(
&self,
known_versions: &HashMap<String, bool>,
) -> Vec<(String, HashMap<String, bool>, Vec<JsonPatch>)> {
let mut updates = Vec::new();
let all_versions: Vec<String> = self.t.keys().cloned().collect();
let mut ancestors_of_known = HashMap::new();
let mut stack: Vec<String> = known_versions.keys().cloned().collect();
while let Some(v) = stack.pop() {
if ancestors_of_known.contains_key(&v) {
continue;
}
if self.t.contains_key(&v) {
ancestors_of_known.insert(v.clone(), true);
if let Some(parents) = self.t.get(&v) {
for p in parents.keys() {
stack.push(p.clone());
}
}
}
}
for version in all_versions {
if ancestors_of_known.contains_key(&version) {
continue;
}
let splices = self.generate_version_splices(&version);
if splices.is_empty() {
}
let patches: Vec<JsonPatch> = splices
.into_iter()
.map(|s| {
let content = match s.insert {
SequenceElems::String(str) => Value::String(str),
SequenceElems::Indices(vec) => {
Value::Null
}
};
JsonPatch {
range: format!("{}:{}", s.pos, s.pos + s.delete_count),
content,
}
})
.collect();
if let Some(parents) = self.t.get(&version) {
updates.push((version, parents.clone(), patches));
}
}
updates
}
pub fn apply_bubbles(&mut self, to_bubble: &HashMap<String, (String, String)>) {
sequence_crdt::apply_bubbles(&mut self.sequence, to_bubble);
for (old_v, (new_v, _)) in to_bubble {
if old_v != new_v {
if let Some(parents) = self.t.remove(old_v) {
self.t
.entry(new_v.clone())
.or_insert_with(HashMap::new)
.extend(parents);
}
if self.current_version.remove(old_v).is_some() {
self.current_version.insert(new_v.clone(), true);
}
}
}
}
}
impl PrunableCrdt for JsonCrdt {
fn apply_patch(&mut self, patch: AntimatterPatch) {
let json_patch = JsonPatch {
range: patch.range,
content: patch.content,
};
let splices = vec![json_patch.to_splice()];
let is_anc = |v: &str| self.t.contains_key(v) || v == "root";
sequence_crdt::add_version(&mut self.sequence, "", splices, is_anc);
}
fn prune(&mut self, version: &str) {
self.t.remove(version);
self.current_version.remove(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<AntimatterPatch>)> {
let updates = self.generate_braid(known_versions);
updates
.into_iter()
.map(|(v, p, patches)| {
(
v,
p,
patches
.into_iter()
.map(|jp| AntimatterPatch {
range: jp.range,
content: jp.content,
})
.collect(),
)
})
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonPatch {
pub range: String,
pub content: Value,
}
impl JsonPatch {
pub fn new(range: &str, content: Value) -> Self {
Self {
range: range.to_string(),
content,
}
}
pub fn to_splice(&self) -> Splice {
let (pos, delete_count) = parse_range(&self.range);
let insert_str = match &self.content {
Value::String(s) => s.clone(),
_ => self.content.to_string(),
};
Splice {
pos,
delete_count,
insert: SequenceElems::String(insert_str),
sort_key: None,
op_type: if delete_count > 0 { 'r' } else { 'i' },
}
}
}
fn parse_range(range: &str) -> (usize, usize) {
if range.contains(':') {
let parts: Vec<&str> = range.split(':').collect();
if parts.len() == 2 {
let start: usize = parts[0].parse().unwrap_or(0);
let end: usize = parts[1].parse().unwrap_or(start);
return (start, end.saturating_sub(start));
}
}
(range.parse().unwrap_or(0), 0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_json_crdt() {
let crdt = JsonCrdt::new("alice");
assert_eq!(crdt.id, "alice");
assert_eq!(crdt.next_seq, 0);
assert!(crdt.t.is_empty());
}
#[test]
fn test_with_content() {
let crdt = JsonCrdt::with_content("alice", "hello");
assert_eq!(crdt.get_content(), "hello");
assert_eq!(crdt.get_length(), 5);
}
#[test]
fn test_add_version() {
let mut crdt = JsonCrdt::with_content("alice", "hello");
let version = crdt.generate_version();
let parents: HashMap<String, bool> = crdt.current_version.clone();
let patch = JsonPatch::new("5", Value::String(" world".to_string()));
crdt.add_version(&version, parents, vec![patch]);
assert!(crdt.t.contains_key(&version));
}
#[test]
fn test_parse_range() {
assert_eq!(parse_range("5"), (5, 0));
assert_eq!(parse_range("5:10"), (5, 5));
assert_eq!(parse_range("0:3"), (0, 3));
}
}