braid-core 0.1.4

Unified Braid Protocol implementation in Rust, including Braid-HTTP, Antimatter CRDT, and BraidFS.
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashSet;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SequenceNode {
    pub version: Option<String>,
    pub elems: SequenceElems,
    pub next: Vec<SequenceNode>,
    pub deleted_by: HashSet<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SequenceElems {
    String(String),
}

impl SequenceNode {
    pub fn text(version: &str, text: &str) -> Self {
        Self {
            version: Some(version.to_string()),
            elems: SequenceElems::String(text.to_string()),
            next: Vec::new(),
            deleted_by: HashSet::new(),
        }
    }
}

pub fn content<F>(node: &SequenceNode, is_visible: F) -> String
where
    F: Fn(&str) -> bool + Copy,
{
    let mut result = String::new();
    let visible = node.version.as_ref().map(|v| is_visible(v)).unwrap_or(true)
        && node.deleted_by.iter().all(|v| !is_visible(v));

    if visible {
        match &node.elems {
            SequenceElems::String(s) => result.push_str(s),
        }
    }

    for child in &node.next {
        result.push_str(&content(child, is_visible));
    }

    result
}

pub fn length<F>(node: &SequenceNode, is_visible: F) -> usize
where
    F: Fn(&str) -> bool + Copy,
{
    let visible = node.version.as_ref().map(|v| is_visible(v)).unwrap_or(true)
        && node.deleted_by.iter().all(|v| !is_visible(v));

    let mut len = if visible {
        match &node.elems {
            SequenceElems::String(s) => s.len(),
        }
    } else {
        0
    };

    for child in &node.next {
        len += length(child, is_visible);
    }

    len
}

pub fn get<F>(node: &SequenceNode, mut pos: usize, is_visible: F) -> Option<char>
where
    F: Fn(&str) -> bool + Copy,
{
    let visible = node.version.as_ref().map(|v| is_visible(v)).unwrap_or(true)
        && node.deleted_by.iter().all(|v| !is_visible(v));

    if visible {
        match &node.elems {
            SequenceElems::String(s) => {
                if pos < s.len() {
                    return s.chars().nth(pos);
                }
                pos -= s.len();
            }
        }
    }

    for child in &node.next {
        let child_len = length(child, is_visible);
        if pos < child_len {
            return get(child, pos, is_visible);
        }
        pos -= child_len;
    }

    None
}

pub fn prune(node: &mut SequenceNode, version: &str) {
    if node.version.as_ref().map(|v| v == version).unwrap_or(false) {
        node.version = None;
    }
    node.deleted_by.remove(version);

    for child in &mut node.next {
        prune(child, version);
    }
}