cojson 0.6.0

Collaborative JSON (A high performance CRDT)
Documentation
use std::hash::Hash;

use crate::{
    mem_use::{MemUsage, MemUser},
    object::ObjectState,
    ops::Op,
};

use super::super::{
    ops::OpKind,
    sort::only_in_first_sorted_list,
    sort::{noncausal_sort, search_from_back},
    undo_redo::UndoRedoState,
};

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct MapState {
    pub(super) create: Option<Op>,
    pub(super) ops_for_key: im::HashMap<String, im::Vector<Op>>,
    undo_redos: UndoRedoState,
}

impl MapState {
    pub fn new() -> MapState {
        MapState {
            create: None,
            ops_for_key: Default::default(),
            undo_redos: UndoRedoState::new(),
        }
    }

    fn insert_op_into_list_for_key(&self, key: String, op: Op) -> Self {
        let new_ops_for_key = self.ops_for_key.alter(
            |maybe_ops_for_key| {
                let ops_for_key = maybe_ops_for_key.unwrap_or_default();
                let insertion_position = search_from_back(&ops_for_key, &op, noncausal_sort);
                match insertion_position {
                    Ok(_) => Some(ops_for_key),
                    Err(pos) => {
                        let mut new_ops_for_key = ops_for_key;
                        new_ops_for_key.insert(pos, op.clone());
                        Some(new_ops_for_key)
                    }
                }
            },
            key,
        );

        MapState {
            ops_for_key: new_ops_for_key,
            ..self.clone()
        }
    }

    fn _sets_not_undone(&self) -> Vec<litl::Val> {
        self.ops_for_key
            .iter()
            .filter_map(move |(_key, ops_for_key)| {
                ops_for_key
                    .iter()
                    .rev()
                    .find(|op| !self.undo_redos.is_undone(op.id))
                    .map(|last_op| match last_op.kind {
                        OpKind::MapSet => last_op.val.as_ref().unwrap()[0].clone(),
                        _ => unreachable!("Has to be map set"),
                    })
            })
            .collect()
    }

    pub fn last_op_for_key(&self, key: &str) -> Option<Op> {
        self.ops_for_key
            .get(key)
            .and_then(|list| list.last().cloned())
    }

    pub fn last_op_for_key_not_undone(&self, key: &str) -> Option<Op> {
        self.ops_for_key.get(key).and_then(|list| {
            list.iter()
                .rev()
                .find(|op| !self.undo_redos.is_undone(op.id))
                .cloned()
        })
    }
}

impl ObjectState for MapState {
    fn apply_op(&self, op: Op) -> Self {
        match (&op.kind, &op.val) {
            (OpKind::MapCreate, None) => {
                if let Some(existing_op) = &self.create {
                    if existing_op.id == op.id {
                        return self.clone();
                    } else {
                        panic!("Already has a create op, with different id")
                    }
                }

                MapState {
                    create: Some(op),
                    ..self.clone()
                }
            }
            (OpKind::MapSet, Some(entry)) => {
                self.insert_op_into_list_for_key(entry[0].clone().as_str().unwrap().to_owned(), op)
            }
            (OpKind::Undo, None) | (OpKind::Redo, None) => MapState {
                undo_redos: self.undo_redos.insert(op),
                ..self.clone()
            },
            _ => panic!("Unexpected op for map {:?}", op),
        }
    }

    fn ops_since(&self, maybe_other: Option<&Self>) -> Vec<Op> {
        let other_create = maybe_other.and_then(|other| other.create.as_ref());

        let maybe_other_ops_for_key = maybe_other.map(|other| &other.ops_for_key);

        let missing_create = if other_create.is_some() {
            None
        } else {
            self.create.clone()
        };
        let missing_ops_for_key = self
            .ops_for_key
            .iter()
            .flat_map(move |(key, entries_for_key)| {
                if let Some(entries_for_key_in_other) = maybe_other_ops_for_key
                    .as_ref()
                    .and_then(|other_ops_for_key| other_ops_for_key.get(key))
                {
                    only_in_first_sorted_list(
                        entries_for_key,
                        entries_for_key_in_other,
                        noncausal_sort,
                    )
                } else {
                    entries_for_key.iter().cloned().collect()
                }
            })
            .collect::<Vec<_>>();

        let missing_undo_redos = self
            .undo_redos
            .ops_since(maybe_other.map(|other| &other.undo_redos));

        missing_create
            .into_iter()
            .chain(missing_ops_for_key)
            .chain(missing_undo_redos)
            .collect()
    }
}

impl MemUser for MapState {
    fn mem_use(&self) -> MemUsage {
        MemUsage::Struct {
            name: "MapState",
            fields: vec![
                ("create", self.create.mem_use()),
                ("ops_for_key", self.ops_for_key.mem_use()),
                ("undo_redos", self.undo_redos.mem_use()),
            ],
        }
    }
}