cojson 0.6.0

Collaborative JSON (A high performance CRDT)
Documentation
use super::list_translator::ListTranslator;
use crate::{
    ops::ObjID,
    ops::OpKind,
    view::{
        jmbl_view::{JMBLViewRef, TranslatorRef},
        value::Value,
    },
};
use std::rc::Rc;

#[derive(Clone)]
pub struct ListView {
    pub(crate) obj_id: ObjID,
    ctx: JMBLViewRef,
}

impl ListView {
    pub(crate) fn new(obj_id: ObjID, ctx: JMBLViewRef) -> ListView {
        ListView { obj_id, ctx }
    }

    fn translator_state(&self) -> Rc<ListTranslator> {
        match self
            .ctx
            .get_mut()
            .current_translator_for(&self.obj_id)
            .expect("Should have translator for list view")
        {
            TranslatorRef::List(list_translator) => list_translator,
            _ => panic!("Expected List Translator"),
        }
    }

    pub fn len(&self) -> usize {
        self.translator_state().len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn val(&self) -> Vec<Value> {
        self.translator_state()
            .iter_items()
            .map(|item| self.ctx.litl_to_value(item.clone()))
            .collect()
    }

    pub fn val_with_deleted(&self) -> Vec<Value> {
        todo!("Not #33 yet implemented");
    }

    pub fn at(&self, idx: usize) -> Value {
        match self.translator_state().at(idx) {
            Some(item) => self.ctx.litl_to_value(item.clone()),
            None => Value::plain(litl::Val::null()),
        }
    }

    pub fn insert_at<I: Into<Value>>(&mut self, idx: usize, item: I) {
        let prev = self.translator_state().prev_op_for_insert_at(idx);
        // TODO(optimization): directly apply ops to correct object
        self.ctx
            .get_mut()
            .apply_edit_op(self.obj_id, prev, OpKind::ListInsertAfter, Some(item.into().to_litl_or_ref()));
    }

    pub fn delete_at(&mut self, idx: usize) {
        let prev = self.translator_state().prev_op_for_delete_at(idx);
        // TODO(optimization): directly apply ops to correct object
        self.ctx
            .get_mut()
            .apply_edit_op(self.obj_id, prev, OpKind::Undo, None);
    }

    pub fn push<I: Into<Value>>(&mut self, item: I) {
        self.insert_at(self.len(), item);
    }

    pub fn pop(&mut self) -> Option<Value> {
        let translator_state = self.translator_state();
        let len = translator_state.len();
        let val = translator_state
            .op_before_index
            .get(len)
            .map(|op| match op.kind {
                OpKind::ListInsertAfter => self
                    .ctx
                    .litl_to_value(op.val.clone().expect("List insert should have value")),
                _ => panic!("Expected ListInsert OpEntry"),
            });
        self.delete_at(len - 1);
        val
    }

    pub fn val_to_litl(&self) -> litl::Val {
        litl::Val::array(
            self.val()
                .into_iter()
                .map(|apparent_value| apparent_value.val_to_litl()),
        )
    }
}

impl std::fmt::Debug for ListView {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ListView")
            .field("value", &self.val())
            .finish()
    }
}

impl std::cmp::PartialEq for ListView {
    fn eq(&self, other: &Self) -> bool {
        self.obj_id == other.obj_id && self.translator_state() == other.translator_state()
    }
}

impl std::cmp::Eq for ListView {}

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