use std::{collections::{HashMap, HashSet}, rc::Rc};
use crate::{
ops::{ObjID, Op, OpKind},
view::{
jmbl_view::{JMBLViewRef, TranslatorRef},
value::Value,
}, io::SrcID,
};
use super::map_translator::MapTranslator;
#[derive(Clone)]
pub struct MapView {
pub(crate) obj_id: ObjID,
ctx: JMBLViewRef,
}
impl MapView {
pub(crate) fn new(obj_id: ObjID, ctx: JMBLViewRef) -> MapView {
MapView { obj_id, ctx }
}
fn translator_state(&self) -> Rc<MapTranslator> {
match self
.ctx
.get_mut()
.current_translator_for(&self.obj_id)
.expect("Should have translator for map view")
{
TranslatorRef::Map(map_translator) => map_translator,
_ => panic!("Expected Map Translator"),
}
}
pub fn val(&self) -> HashMap<String, Value> {
self.translator_state()
.iter_entries()
.map(|(key, val)| (key, self.ctx.litl_to_value(val)))
.collect()
}
pub fn insert<K: Into<String>, V: Into<Value>>(&mut self, key: K, val: V) {
let key_val = key.into();
if let Some(last_op) = self.translator_state().last_op_for_key(&key_val) {
self.ctx.get_mut().apply_edit_op(
self.obj_id,
last_op.id,
OpKind::MapSet,
Some(litl::Val::array(vec![litl::Val::string(key_val), val.into().to_litl_or_ref()])),
);
} else {
let create_op_id = self
.translator_state()
.create
.clone()
.expect("Expected create op on insert")
.id;
self.ctx.get_mut().apply_edit_op(
self.obj_id,
create_op_id,
OpKind::MapSet,
Some(litl::Val::array(vec![litl::Val::string(key_val), val.into().to_litl_or_ref()])),
);
}
}
pub fn delete<I: Into<String>>(&mut self, key: I) {
let key_val = key.into();
if let Some(last_op) = self.translator_state().last_op_for_key(&key_val) {
self.ctx.get_mut().apply_edit_op(
self.obj_id,
last_op.id,
OpKind::MapSet,
Some(litl::Val::array(vec![key_val.into(), litl::Value::Null])),
);
}
}
pub fn get<I: Into<String>>(&self, key: I) -> Value {
self.apparent_value_for_key(key.into())
}
fn apparent_value_for_key(&self, key: String) -> Value {
match self.translator_state().last_op_for_key_not_undone(&key) {
Some(Op {
kind: OpKind::MapSet,
val: entry,
..
}) => self.ctx.litl_to_value(entry.unwrap()[1].clone()),
None => Value::plain(litl::Val::null()),
Some(_other_op) => unreachable!("Only map ops expected"),
}
}
pub fn val_to_litl(&self) -> litl::Val {
litl::Val::object(
self.val()
.into_iter()
.map(|(key, val)| (key, val.val_to_litl())),
)
}
pub fn entries(&self) -> Vec<(String, litl::Val)> {
self.translator_state().iter_entries().collect()
}
pub fn affecting_logs_for_key(&self, key: &str) -> HashSet<SrcID> {
(*self.translator_state()).map.ops_for_key.get(key).map(|ops|
ops.iter().map(|op| op.id.0).collect()
).unwrap_or_default()
}
pub fn last_op_for_key_not_undone(&self, key: &str) -> Option<Op> {
self.translator_state().last_op_for_key_not_undone(key)
}
}
impl std::fmt::Debug for MapView {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MapView")
.field("value", &self.val())
.finish()
}
}
impl std::cmp::PartialEq for MapView {
fn eq(&self, other: &Self) -> bool {
self.translator_state().map == other.translator_state().map
}
}
impl std::cmp::Eq for MapView {}
impl std::hash::Hash for MapView {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.translator_state().map.hash(state);
}
}