use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct SequenceNode {
pub version: Option<String>,
pub sort_key: Option<String>,
pub elems: SequenceElems,
pub end_cap: bool,
pub deleted_by: HashSet<String>,
pub nexts: Vec<Box<SequenceNode>>,
pub next: Option<Box<SequenceNode>>,
}
#[derive(Debug, Clone)]
pub enum SequenceElems {
String(String),
Indices(Vec<usize>),
}
impl SequenceElems {
pub fn len(&self) -> usize {
match self {
SequenceElems::String(s) => s.len(),
SequenceElems::Indices(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn slice(&self, start: usize, end: usize) -> Self {
match self {
SequenceElems::String(s) => {
SequenceElems::String(s.chars().skip(start).take(end - start).collect())
}
SequenceElems::Indices(v) => SequenceElems::Indices(v[start..end].to_vec()),
}
}
pub fn concat(&self, other: &Self) -> Self {
match (self, other) {
(SequenceElems::String(a), SequenceElems::String(b)) => {
SequenceElems::String(format!("{}{}", a, b))
}
(SequenceElems::Indices(a), SequenceElems::Indices(b)) => {
let mut result = a.clone();
result.extend(b);
SequenceElems::Indices(result)
}
_ => panic!("Cannot concat different element types"),
}
}
pub fn empty_like(&self) -> Self {
match self {
SequenceElems::String(_) => SequenceElems::String(String::new()),
SequenceElems::Indices(_) => SequenceElems::Indices(Vec::new()),
}
}
}
impl SequenceNode {
pub fn new(
version: Option<String>,
elems: SequenceElems,
end_cap: bool,
sort_key: Option<String>,
) -> Self {
Self {
version,
sort_key,
elems,
end_cap,
deleted_by: HashSet::new(),
nexts: Vec::new(),
next: None,
}
}
pub fn text(version: &str, text: &str) -> Self {
Self::new(
Some(version.to_string()),
SequenceElems::String(text.to_string()),
false,
None,
)
}
pub fn effective_sort_key(&self) -> Option<&str> {
self.sort_key.as_deref().or(self.version.as_deref())
}
}
#[derive(Debug, Clone)]
pub struct Splice {
pub pos: usize,
pub delete_count: usize,
pub insert: SequenceElems,
pub sort_key: Option<String>,
pub op_type: char,
}
pub fn length<F>(root: &SequenceNode, is_anc: F) -> usize
where
F: Fn(&str) -> bool + Copy,
{
let mut count = 0;
traverse(root, is_anc, |node, _, _, _, _, deleted| {
if !deleted {
count += node.elems.len();
}
true
});
count
}
pub fn get<F>(root: &SequenceNode, i: usize, is_anc: F) -> Option<char>
where
F: Fn(&str) -> bool + Copy,
{
let mut offset = 0;
let mut result = None;
traverse(root, is_anc, |node, _, _, _, _, deleted| {
if deleted {
return true;
}
if i < offset + node.elems.len() {
if let SequenceElems::String(s) = &node.elems {
result = s.chars().nth(i - offset);
}
return false; }
offset += node.elems.len();
true
});
result
}
pub fn traverse<F, C>(root: &SequenceNode, is_anc: F, mut callback: C)
where
F: Fn(&str) -> bool + Copy,
C: FnMut(&SequenceNode, usize, bool, Option<&str>, Option<&str>, bool) -> bool,
{
let mut offset = 0;
fn helper<F, C>(
node: &SequenceNode,
_prev: Option<&SequenceNode>,
version: Option<&str>,
is_anc: F,
callback: &mut C,
offset: &mut usize,
) -> bool
where
F: Fn(&str) -> bool + Copy,
C: FnMut(&SequenceNode, usize, bool, Option<&str>, Option<&str>, bool) -> bool,
{
let has_nexts = node
.nexts
.iter()
.any(|next| next.version.as_ref().map_or(false, |v| is_anc(v)));
let deleted = node.deleted_by.iter().any(|v| is_anc(v));
if !callback(node, *offset, has_nexts, None, version, deleted) {
return false;
}
if !deleted {
*offset += node.elems.len();
}
for next in &node.nexts {
if next.version.as_ref().map_or(false, |v| is_anc(v)) {
if !helper(
next,
Some(node),
next.version.as_deref(),
is_anc,
callback,
offset,
) {
return false;
}
}
}
if let Some(ref next) = node.next {
if !helper(next, Some(node), version, is_anc, callback, offset) {
return false;
}
}
true
}
helper(
root,
None,
root.version.as_deref(),
is_anc,
&mut callback,
&mut offset,
);
}
pub fn content<F>(root: &SequenceNode, is_anc: F) -> String
where
F: Fn(&str) -> bool + Copy,
{
let mut result = String::new();
traverse(root, is_anc, |node, _, _, _, _, deleted| {
if !deleted {
if let SequenceElems::String(s) = &node.elems {
result.push_str(s);
}
}
true
});
result
}
pub fn break_node(
node: &mut SequenceNode,
x: usize,
end_cap: bool,
new_next: Option<Box<SequenceNode>>,
) -> Box<SequenceNode> {
let tail_elems = node.elems.slice(x, node.elems.len());
let mut tail = Box::new(SequenceNode::new(None, tail_elems, node.end_cap, None));
tail.deleted_by = node.deleted_by.clone();
tail.nexts = std::mem::take(&mut node.nexts);
tail.next = node.next.take();
node.elems = node.elems.slice(0, x);
node.end_cap = end_cap;
node.nexts = match new_next {
Some(n) => vec![n],
None => Vec::new(),
};
node.next = Some(tail.clone());
tail
}
pub fn add_version<F>(
root: &mut SequenceNode,
version: &str,
splices: Vec<Splice>,
is_anc: F,
) -> Vec<Splice>
where
F: Fn(&str) -> bool + Copy,
{
let mut rebased_splices = Vec::new();
if splices.is_empty() {
return rebased_splices;
}
let mut si = 0; let mut delete_up_to = 0;
let mut offset = 0;
fn process_splices(
node: &mut SequenceNode,
splices: &[Splice],
si: &mut usize,
delete_up_to: &mut usize,
offset: &mut usize,
version: &str,
is_anc: impl Fn(&str) -> bool + Copy,
) {
if *si >= splices.len() {
return;
}
let s = &splices[*si];
let deleted = node.deleted_by.iter().any(|v| is_anc(v));
if deleted {
if s.delete_count == 0 && s.pos == *offset {
let new_node = Box::new(SequenceNode::new(
Some(version.to_string()),
s.insert.clone(),
false,
s.sort_key.clone(),
));
node.nexts.push(new_node);
*si += 1;
}
return;
}
if s.delete_count == 0 {
let d = s.pos as isize - (*offset + node.elems.len()) as isize;
if d > 0 {
return; }
if d == 0 && !node.end_cap && !node.nexts.is_empty() {
return; }
let new_node = Box::new(SequenceNode::new(
Some(version.to_string()),
s.insert.clone(),
false,
s.sort_key.clone(),
));
if d == 0 && !node.end_cap {
node.nexts.push(new_node);
} else {
let break_pos = s.pos - *offset;
break_node(node, break_pos, false, Some(new_node));
}
*si += 1;
return;
}
if *delete_up_to <= *offset {
let d = s.pos as isize - (*offset + node.elems.len()) as isize;
if d > 0 || (d == 0) {
return;
}
*delete_up_to = s.pos + s.delete_count;
if !s.insert.is_empty() {
let new_node = Box::new(SequenceNode::new(
Some(version.to_string()),
s.insert.clone(),
false,
s.sort_key.clone(),
));
let break_pos = s.pos - *offset;
break_node(node, break_pos, true, Some(new_node));
return;
} else if s.pos != *offset {
let break_pos = s.pos - *offset;
break_node(node, break_pos, false, None);
return;
}
}
if *delete_up_to > *offset {
if *delete_up_to <= *offset + node.elems.len() {
if *delete_up_to < *offset + node.elems.len() {
let break_pos = *delete_up_to - *offset;
break_node(node, break_pos, false, None);
}
*si += 1;
}
node.deleted_by.insert(version.to_string());
}
}
process_splices(
root,
&splices,
&mut si,
&mut delete_up_to,
&mut offset,
version,
is_anc,
);
rebased_splices
}
pub fn generate_braid<F>(root: &SequenceNode, version: &str, is_anc: F) -> Vec<Splice>
where
F: Fn(&str) -> bool + Copy,
{
let mut splices = Vec::new();
let mut offset = 0;
fn helper<F>(
node: &SequenceNode,
_version: Option<&str>,
target_version: &str,
is_anc: F,
splices: &mut Vec<Splice>,
offset: &mut usize,
end_cap: bool,
) where
F: Fn(&str) -> bool + Copy,
{
let node_version = node.version.as_deref();
if node_version == Some(target_version) {
let splice = Splice {
pos: *offset,
delete_count: 0,
insert: node.elems.clone(),
sort_key: node.sort_key.clone(),
op_type: if end_cap { 'r' } else { 'i' },
};
splices.push(splice);
}
else if node.deleted_by.contains(target_version) && !node.elems.is_empty() {
let splice = Splice {
pos: *offset,
delete_count: node.elems.len(),
insert: node.elems.empty_like(),
sort_key: None,
op_type: 'd',
};
splices.push(splice);
}
if (node_version.is_none() || node_version.map_or(false, |v| is_anc(v)))
&& !node.deleted_by.iter().any(|v| is_anc(v))
{
*offset += node.elems.len();
}
for next in &node.nexts {
helper(
next,
next.version.as_deref(),
target_version,
is_anc,
splices,
offset,
node.end_cap,
);
}
if let Some(ref next) = node.next {
helper(
next,
_version,
target_version,
is_anc,
splices,
offset,
false,
);
}
}
helper(
root,
root.version.as_deref(),
version,
is_anc,
&mut splices,
&mut offset,
false,
);
for s in &mut splices {
if s.op_type == 'r' && s.delete_count == 0 {
s.delete_count = 1;
}
}
splices
}
pub fn apply_bubbles(root: &mut SequenceNode, to_bubble: &HashMap<String, (String, String)>) {
fn rename_versions(node: &mut SequenceNode, to_bubble: &HashMap<String, (String, String)>) {
if let Some(ref v) = node.version {
if let Some((bottom, _top)) = to_bubble.get(v) {
if bottom != v {
if node.sort_key.is_none() {
node.sort_key = node.version.take();
}
node.version = Some(bottom.clone());
}
}
}
let old_deleted: Vec<String> = node.deleted_by.iter().cloned().collect();
for v in old_deleted {
if let Some((bottom, _)) = to_bubble.get(&v) {
node.deleted_by.remove(&v);
node.deleted_by.insert(bottom.clone());
}
}
for next in &mut node.nexts {
rename_versions(next, to_bubble);
}
if let Some(ref mut next) = node.next {
rename_versions(next, to_bubble);
}
}
rename_versions(root, to_bubble);
fn merge_nodes(node: &mut SequenceNode) {
if let Some(first_next) = node.nexts.first() {
if first_next.version == node.version {
let same_version_nexts: Vec<_> = node
.nexts
.iter()
.filter(|n| n.version == node.version)
.cloned()
.collect();
if same_version_nexts.len() == node.nexts.len() {
node.nexts.clear();
}
}
}
while let Some(ref mut next) = node.next {
if node.nexts.is_empty()
&& !node.elems.is_empty()
&& !next.elems.is_empty()
&& node.deleted_by.iter().all(|v| next.deleted_by.contains(v))
&& next.deleted_by.iter().all(|v| node.deleted_by.contains(v))
{
node.elems = node.elems.concat(&next.elems);
node.end_cap = next.end_cap;
node.nexts = std::mem::take(&mut next.nexts);
node.next = next.next.take();
} else {
break;
}
}
for next in &mut node.nexts {
merge_nodes(next);
}
if let Some(ref mut next) = node.next {
merge_nodes(next);
}
}
merge_nodes(root);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_node() {
let node = SequenceNode::text("alice1", "hello");
assert_eq!(node.version, Some("alice1".to_string()));
assert!(matches!(node.elems, SequenceElems::String(ref s) if s == "hello"));
assert!(!node.end_cap);
assert!(node.deleted_by.is_empty());
assert!(node.nexts.is_empty());
assert!(node.next.is_none());
}
#[test]
fn test_content() {
let node = SequenceNode::text("alice1", "hello");
let content = content(&node, |_| true);
assert_eq!(content, "hello");
}
#[test]
fn test_length() {
let node = SequenceNode::text("alice1", "hello");
let len = length(&node, |_| true);
assert_eq!(len, 5);
}
#[test]
fn test_get() {
let node = SequenceNode::text("alice1", "hello");
assert_eq!(get(&node, 0, |_| true), Some('h'));
assert_eq!(get(&node, 4, |_| true), Some('o'));
assert_eq!(get(&node, 5, |_| true), None);
}
#[test]
fn test_deleted_node() {
let mut node = SequenceNode::text("alice1", "hello");
node.deleted_by.insert("bob1".to_string());
let len = length(&node, |v| v == "alice1" || v == "bob1");
assert_eq!(len, 0);
let len_without_delete = length(&node, |v| v == "alice1");
assert_eq!(len_without_delete, 5); }
}