use std::{cell::RefCell, ops::Deref};
use crate::{
mem_use::{MemUsage, MemUser},
object::ObjectState,
ops::{Op, OpWithTarget},
sparse_vector::SparseOpIDSet,
text::text_state::TextState,
view::jmbl_view::{JMBLView, JMBLViewRef},
Value,
};
use super::{
list::list_state::ListState,
map::map_state::MapState,
object::AnyObjectState,
ops::{ObjID, OpKind},
};
#[derive(Clone)]
pub struct ReadableJMBL {
pub(crate) state: JMBLState,
read_view: RefCell<Option<JMBLViewRef>>,
}
impl ReadableJMBL {
pub fn new() -> ReadableJMBL {
ReadableJMBL {
state: JMBLState::new(),
read_view: RefCell::new(None),
}
}
pub(crate) fn new_from(inner: JMBLState) -> ReadableJMBL {
ReadableJMBL {
state: inner,
read_view: RefCell::new(None),
}
}
pub fn get_object(&self, obj_id: &ObjID) -> Value {
self.read_view
.borrow_mut()
.get_or_insert_with(|| JMBLViewRef::new(JMBLView::new(None, self.state.clone())))
.obj_id_to_value(obj_id)
}
pub fn get_root(&self) -> Value {
self.root
.as_ref()
.map(|(root, _)| self.get_object(root))
.unwrap_or_else(|| Value::plain(litl::Val::null()))
}
pub fn with_ops_applied<'a, I: IntoIterator<Item = &'a OpWithTarget>>(&self, ops: I) -> Self {
ReadableJMBL::new_from(self.state.with_ops_applied(ops))
}
}
impl Deref for ReadableJMBL {
type Target = JMBLState;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl std::fmt::Debug for ReadableJMBL {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.state.fmt(f)
}
}
impl Default for ReadableJMBL {
fn default() -> Self {
ReadableJMBL::new()
}
}
#[derive(Clone, Debug)]
pub struct JMBLState {
pub(crate) objects: im::HashMap<ObjID, AnyObjectState>,
seen_ops: SparseOpIDSet,
pub(crate) root: Option<(ObjID, Op)>,
disconnected: Vec<OpWithTarget>,
}
impl JMBLState {
pub fn new() -> Self {
JMBLState {
objects: Default::default(),
seen_ops: Default::default(),
root: None,
disconnected: vec![],
}
}
pub fn with_ops_applied<'a, I: IntoIterator<Item = &'a OpWithTarget>>(&self, ops: I) -> Self {
let mut curr = self.clone();
let mut made_progress_with_disconnected = true;
let mut disconnected = ops.into_iter().cloned().collect::<Vec<_>>();
disconnected.extend(std::mem::take(&mut curr.disconnected).into_iter());
while made_progress_with_disconnected {
made_progress_with_disconnected = false;
disconnected.retain(|op_with_target| {
if curr.seen_ops.contains(&op_with_target.op.id) {
made_progress_with_disconnected = true;
return false;
};
if op_with_target
.dependencies()
.all(|dep| curr.seen_ops.contains(&dep))
{
made_progress_with_disconnected = true;
curr.mutably_insert_op(op_with_target);
false
} else {
true
}
})
}
curr.disconnected = disconnected;
curr
}
fn mutably_insert_op(&mut self, op_with_target: &OpWithTarget) {
let OpWithTarget { target_obj_id, op } = op_with_target;
match op.kind {
OpKind::SetRoot => {
let root_id = litl::from_val(op.val.clone().expect("Expected root object id"))
.expect("Expected root value to be ObjID");
self.root = Some((root_id, op.clone()));
}
OpKind::ListCreate | OpKind::TextCreate | OpKind::MapCreate => {
let mut object_state = match op.kind {
OpKind::ListCreate => AnyObjectState::List(ListState::new()),
OpKind::TextCreate => AnyObjectState::Text(TextState::new()),
OpKind::MapCreate => AnyObjectState::Map(MapState::new()),
_ => unreachable!("Has to be create op here"),
};
object_state = object_state.apply_ops(Some(op.clone()));
self.objects.insert(*target_obj_id, object_state);
}
_ => {
let target = self
.objects
.get_mut(target_obj_id)
.expect("Should have object");
assert!(self.seen_ops.contains(&op.prev));
*target = target.apply_ops(Some(op.clone()));
}
}
self.seen_ops.insert(op.id);
}
pub fn ops_since<'a>(&'a self, other: &'a Self) -> impl Iterator<Item = OpWithTarget> + 'a {
self.objects.iter().flat_map(move |(obj_id, obj_state)| {
let ops_since = obj_state.ops_since(other.objects.get(obj_id));
ops_since
.into_iter()
.map(|op| OpWithTarget {
target_obj_id: *obj_id,
op,
})
.collect::<Vec<_>>()
})
}
}
impl Default for JMBLState {
fn default() -> Self {
JMBLState::new()
}
}
impl MemUser for JMBLState {
fn mem_use(&self) -> MemUsage {
MemUsage::Struct {
name: "JMBLState",
fields: vec![
("objects", self.objects.mem_use()),
("seen_ops", self.seen_ops.mem_use()),
],
}
}
}