use loro::{LoroDoc, LoroList, LoroMap, LoroResult, LoroValue, ExportMode, ValueOrContainer, UndoManager};
use crate::shapes::Shape;
use super::convert::{shape_to_loro, shape_from_loro};
pub const SHAPES_KEY: &str = "shapes";
pub const Z_ORDER_KEY: &str = "z_order";
pub const NAME_KEY: &str = "name";
pub struct CrdtDocument {
doc: LoroDoc,
undo_manager: UndoManager,
}
impl CrdtDocument {
pub fn new() -> Self {
let doc = LoroDoc::new();
let mut undo_manager = UndoManager::new(&doc);
undo_manager.set_max_undo_steps(100);
undo_manager.set_merge_interval(300); Self { doc, undo_manager }
}
pub fn from_snapshot(bytes: &[u8]) -> LoroResult<Self> {
let doc = LoroDoc::new();
doc.import(bytes)?;
let mut undo_manager = UndoManager::new(&doc);
undo_manager.set_max_undo_steps(100);
undo_manager.set_merge_interval(300);
Ok(Self { doc, undo_manager })
}
pub fn loro_doc(&self) -> &LoroDoc {
&self.doc
}
pub fn loro_doc_mut(&mut self) -> &mut LoroDoc {
&mut self.doc
}
fn shapes_map(&self) -> LoroMap {
self.doc.get_map(SHAPES_KEY)
}
fn z_order_list(&self) -> LoroList {
self.doc.get_list(Z_ORDER_KEY)
}
pub fn shape_count(&self) -> usize {
self.shapes_map().len()
}
pub fn z_order(&self) -> Vec<String> {
let list = self.z_order_list();
let mut result = Vec::with_capacity(list.len());
for i in 0..list.len() {
if let Some(ValueOrContainer::Value(LoroValue::String(id))) = list.get(i) {
result.push(id.to_string());
}
}
result
}
pub fn add_shape(&mut self, shape: &Shape) -> LoroResult<()> {
let id = shape.id().to_string();
let shapes = self.shapes_map();
let z_order = self.z_order_list();
let shape_map = shapes.insert_container(&id, LoroMap::new())?;
shape_to_loro(shape, &shape_map)?;
z_order.push(LoroValue::String(id.into()))?;
self.doc.commit();
Ok(())
}
pub fn remove_shape(&mut self, id: &str) -> LoroResult<()> {
let shapes = self.shapes_map();
let z_order = self.z_order_list();
shapes.delete(&id)?;
for i in 0..z_order.len() {
if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i) {
if s.as_ref() == id {
z_order.delete(i, 1)?;
break;
}
}
}
self.doc.commit();
Ok(())
}
pub fn get_shape(&self, id: &str) -> Option<Shape> {
let shapes = self.shapes_map();
let shapes_value = shapes.get_deep_value();
if let LoroValue::Map(map) = shapes_value {
if let Some(shape_value) = map.get(id) {
if let LoroValue::Map(shape_map) = shape_value {
return shape_from_loro(shape_map);
}
}
}
None
}
pub fn update_shape(&mut self, shape: &Shape) -> LoroResult<()> {
let id = shape.id().to_string();
let shapes = self.shapes_map();
shapes.delete(&id)?;
let shape_map = shapes.insert_container(&id, LoroMap::new())?;
shape_to_loro(shape, &shape_map)?;
self.doc.commit();
Ok(())
}
pub fn shapes_ordered(&self) -> Vec<Shape> {
let z_order = self.z_order();
let mut shapes = Vec::with_capacity(z_order.len());
for id in z_order {
if let Some(shape) = self.get_shape(&id) {
shapes.push(shape);
}
}
shapes
}
pub fn bring_to_front(&mut self, id: &str) -> LoroResult<()> {
let z_order = self.z_order_list();
for i in 0..z_order.len() {
if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i) {
if s.as_ref() == id {
z_order.delete(i, 1)?;
break;
}
}
}
z_order.push(LoroValue::String(id.to_string().into()))?;
self.doc.commit();
Ok(())
}
pub fn send_to_back(&mut self, id: &str) -> LoroResult<()> {
let z_order = self.z_order_list();
for i in 0..z_order.len() {
if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i) {
if s.as_ref() == id {
z_order.delete(i, 1)?;
break;
}
}
}
z_order.insert(0, LoroValue::String(id.to_string().into()))?;
self.doc.commit();
Ok(())
}
pub fn bring_forward(&mut self, id: &str) -> LoroResult<bool> {
let z_order = self.z_order_list();
let len = z_order.len();
let mut pos = None;
for i in 0..len {
if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i) {
if s.as_ref() == id {
pos = Some(i);
break;
}
}
}
if let Some(i) = pos {
if i < len - 1 {
let next_id = if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i + 1) {
s.to_string()
} else {
return Ok(false);
};
z_order.delete(i, 2)?;
z_order.insert(i, LoroValue::String(id.to_string().into()))?;
z_order.insert(i, LoroValue::String(next_id.into()))?;
self.doc.commit();
return Ok(true);
}
}
Ok(false)
}
pub fn send_backward(&mut self, id: &str) -> LoroResult<bool> {
let z_order = self.z_order_list();
let mut pos = None;
for i in 0..z_order.len() {
if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i) {
if s.as_ref() == id {
pos = Some(i);
break;
}
}
}
if let Some(i) = pos {
if i > 0 {
let prev_id = if let Some(ValueOrContainer::Value(LoroValue::String(s))) = z_order.get(i - 1) {
s.to_string()
} else {
return Ok(false);
};
z_order.delete(i - 1, 2)?;
z_order.insert(i - 1, LoroValue::String(id.to_string().into()))?;
z_order.insert(i - 1, LoroValue::String(prev_id.into()))?;
self.doc.commit();
return Ok(true);
}
}
Ok(false)
}
pub fn export_snapshot(&self) -> Vec<u8> {
self.doc.export(ExportMode::Snapshot).unwrap_or_default()
}
pub fn export_updates(&self, since: &loro::VersionVector) -> Vec<u8> {
self.doc.export(ExportMode::updates(since)).unwrap_or_default()
}
pub fn import(&mut self, bytes: &[u8]) -> LoroResult<()> {
self.doc.import(bytes)?;
Ok(())
}
pub fn version(&self) -> loro::VersionVector {
self.doc.oplog_vv()
}
pub fn name(&self) -> String {
let text = self.doc.get_text(NAME_KEY);
text.to_string()
}
pub fn set_name(&mut self, name: &str) -> LoroResult<()> {
let text = self.doc.get_text(NAME_KEY);
let len = text.len_unicode();
if len > 0 {
text.delete(0, len)?;
}
text.insert(0, name)?;
self.doc.commit();
Ok(())
}
pub fn clear(&mut self) -> LoroResult<()> {
let z_order = self.z_order_list();
let len = z_order.len();
if len > 0 {
z_order.delete(0, len)?;
}
let shapes = self.shapes_map();
let keys: Vec<String> = {
let value = shapes.get_deep_value();
if let LoroValue::Map(map) = value {
map.keys().cloned().collect()
} else {
vec![]
}
};
for key in keys {
shapes.delete(&key)?;
}
self.doc.commit();
Ok(())
}
pub fn undo(&mut self) -> bool {
self.undo_manager.undo().unwrap_or(false)
}
pub fn redo(&mut self) -> bool {
self.undo_manager.redo().unwrap_or(false)
}
pub fn can_undo(&self) -> bool {
self.undo_manager.can_undo()
}
pub fn can_redo(&self) -> bool {
self.undo_manager.can_redo()
}
pub fn undo_count(&self) -> usize {
self.undo_manager.undo_count()
}
pub fn redo_count(&self) -> usize {
self.undo_manager.redo_count()
}
pub fn record_checkpoint(&mut self) {
let _ = self.undo_manager.record_new_checkpoint();
}
pub fn start_undo_group(&mut self) {
let _ = self.undo_manager.group_start();
}
pub fn end_undo_group(&mut self) {
self.undo_manager.group_end();
}
pub fn clear_undo_history(&self) {
self.undo_manager.clear();
}
}
impl Default for CrdtDocument {
fn default() -> Self {
Self::new()
}
}
impl Clone for CrdtDocument {
fn clone(&self) -> Self {
let bytes = self.export_snapshot();
Self::from_snapshot(&bytes).unwrap_or_else(|_| Self::new())
}
}