cojson 0.6.0

Collaborative JSON (A high performance CRDT)
Documentation
use std::{
    collections::{HashMap, HashSet},
    hash::Hash,
};

use super::{
    super::ops::{Op, OpID, OpKind},
    list_state::ListState,
};

pub(crate) struct ListTranslator {
    pub(super) list: ListState,
    pub op_before_index: Vec<Op>,
    pub index_after_op_id: HashMap<OpID, usize>,
}

impl ListTranslator {
    pub fn new(list: ListState) -> ListTranslator {
        // TODO(optimization): a lot of these can be looked up directly on state
        let mut op_before_index = Vec::new();
        let mut index_after_op_hash = HashMap::new();
        let mut undone = HashSet::new();

        if let Some(create_op) = &list.create {
            index_after_op_hash.insert(create_op.id, 0);
            op_before_index.insert(0, create_op.clone());
        }

        for undone_hash in list.undo_redos.keys() {
            if list.inserts.has(undone_hash) && list.undo_redos.is_undone(*undone_hash) {
                undone.insert(*undone_hash);
            }
        }

        let mut i = 1;
        for insert_op in list.inserts_in_order() {
            index_after_op_hash.insert(insert_op.id, i);
            if !undone.contains(&insert_op.id) {
                op_before_index.insert(i, insert_op);
                i += 1;
            }
        }

        ListTranslator {
            list,
            op_before_index,
            index_after_op_id: index_after_op_hash,
        }
    }

    pub fn len(&self) -> usize {
        self.op_before_index.len().saturating_sub(1)
    }

    pub fn at(&self, index: usize) -> Option<&litl::Val> {
        self.op_before_index
            .get(index + 1)
            .map(|op| match &op.kind {
                OpKind::ListInsertAfter => op.val.as_ref().expect("Insert op should have value"),
                _ => unreachable!("Should only have insert ops"),
            })
    }

    pub fn prev_op_for_insert_at(&self, index: usize) -> OpID {
        self.op_before_index[index].id
    }

    pub fn prev_op_for_delete_at(&self, index: usize) -> OpID {
        self.op_before_index[index + 1].id
    }

    pub fn iter_items(&self) -> impl Iterator<Item = &litl::Val> {
        self.op_before_index[1..].iter().map(|op| match op.kind {
            OpKind::ListInsertAfter => op.val.as_ref().expect("Insert op should have value"),
            _ => panic!("Expected ListInsert"),
        })
    }
}

impl PartialEq for ListTranslator {
    fn eq(&self, other: &Self) -> bool {
        self.list == other.list
    }
}

impl Hash for ListTranslator {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.list.hash(state)
    }
}