mod schema;
mod convert;
pub use schema::{CrdtDocument, SHAPES_KEY, Z_ORDER_KEY, NAME_KEY};
pub use convert::{shape_to_loro, shape_from_loro};
pub use loro::{VersionVector, ExportMode};
#[cfg(test)]
mod tests {
use super::*;
use crate::shapes::{Rectangle, Shape, ShapeStyle, SerializableColor, Sloppiness};
use kurbo::Point;
#[test]
fn test_crdt_document_creation() {
let doc = CrdtDocument::new();
assert!(doc.shape_count() == 0);
}
#[test]
fn test_add_shape_to_crdt() {
let mut doc = CrdtDocument::new();
let rect = Rectangle::new(Point::new(100.0, 200.0), 50.0, 30.0);
let shape = Shape::Rectangle(rect);
let id = shape.id();
doc.add_shape(&shape).expect("Failed to add shape");
assert_eq!(doc.shape_count(), 1);
assert!(doc.z_order().contains(&id.to_string()));
}
#[test]
fn test_roundtrip_rectangle() {
let mut doc = CrdtDocument::new();
let mut rect = Rectangle::new(Point::new(100.0, 200.0), 150.0, 80.0);
rect.corner_radius = 16.0;
rect.style = ShapeStyle {
stroke_color: SerializableColor::new(255, 0, 0, 255),
stroke_width: 3.0,
fill_color: Some(SerializableColor::new(0, 255, 0, 128)),
sloppiness: Sloppiness::Artist,
seed: 12345, };
let original = Shape::Rectangle(rect);
let id = original.id();
doc.add_shape(&original).expect("Failed to add shape");
let recovered = doc.get_shape(&id.to_string()).expect("Shape not found");
match recovered {
Shape::Rectangle(r) => {
assert!((r.position.x - 100.0).abs() < 0.001);
assert!((r.position.y - 200.0).abs() < 0.001);
assert!((r.width - 150.0).abs() < 0.001);
assert!((r.height - 80.0).abs() < 0.001);
assert!((r.corner_radius - 16.0).abs() < 0.001);
assert_eq!(r.style.stroke_color.r, 255);
assert_eq!(r.style.stroke_color.g, 0);
assert_eq!(r.style.stroke_width as i32, 3);
assert!(r.style.fill_color.is_some());
assert_eq!(r.style.sloppiness, Sloppiness::Artist);
}
_ => panic!("Expected Rectangle, got different shape type"),
}
}
#[test]
fn test_remove_shape() {
let mut doc = CrdtDocument::new();
let rect = Rectangle::new(Point::new(0.0, 0.0), 100.0, 100.0);
let shape = Shape::Rectangle(rect);
let id = shape.id().to_string();
doc.add_shape(&shape).expect("Failed to add shape");
assert_eq!(doc.shape_count(), 1);
doc.remove_shape(&id).expect("Failed to remove shape");
assert_eq!(doc.shape_count(), 0);
assert!(!doc.z_order().contains(&id));
}
#[test]
fn test_z_order_manipulation() {
let mut doc = CrdtDocument::new();
let rect1 = Shape::Rectangle(Rectangle::new(Point::new(0.0, 0.0), 50.0, 50.0));
let rect2 = Shape::Rectangle(Rectangle::new(Point::new(25.0, 25.0), 50.0, 50.0));
let id1 = rect1.id().to_string();
let id2 = rect2.id().to_string();
doc.add_shape(&rect1).expect("Failed to add shape 1");
doc.add_shape(&rect2).expect("Failed to add shape 2");
let order = doc.z_order();
assert_eq!(order.len(), 2);
assert_eq!(order[0], id1);
assert_eq!(order[1], id2);
doc.bring_to_front(&id1).expect("Failed to bring to front");
let order = doc.z_order();
assert_eq!(order[0], id2);
assert_eq!(order[1], id1);
doc.send_to_back(&id1).expect("Failed to send to back");
let order = doc.z_order();
assert_eq!(order[0], id1);
assert_eq!(order[1], id2);
}
#[test]
fn test_export_import() {
let mut doc = CrdtDocument::new();
let rect = Shape::Rectangle(Rectangle::new(Point::new(10.0, 20.0), 100.0, 50.0));
doc.add_shape(&rect).expect("Failed to add shape");
let bytes = doc.export_snapshot();
let doc2 = CrdtDocument::from_snapshot(&bytes).expect("Failed to import");
assert_eq!(doc2.shape_count(), 1);
}
#[test]
fn test_crdt_undo_add_shape() {
let mut doc = CrdtDocument::new();
let rect = Rectangle::new(Point::new(100.0, 200.0), 50.0, 30.0);
let shape = Shape::Rectangle(rect);
doc.add_shape(&shape).expect("Failed to add shape");
assert_eq!(doc.shape_count(), 1);
assert!(doc.can_undo());
assert!(doc.undo());
assert_eq!(doc.shape_count(), 0);
assert!(doc.can_redo());
assert!(doc.redo());
assert_eq!(doc.shape_count(), 1);
}
#[test]
fn test_crdt_undo_remove_shape() {
let mut doc = CrdtDocument::new();
let rect = Rectangle::new(Point::new(0.0, 0.0), 100.0, 100.0);
let shape = Shape::Rectangle(rect);
let id = shape.id().to_string();
doc.add_shape(&shape).expect("Failed to add shape");
doc.clear_undo_history();
doc.remove_shape(&id).expect("Failed to remove shape");
assert_eq!(doc.shape_count(), 0);
assert!(doc.undo());
assert_eq!(doc.shape_count(), 1);
}
#[test]
fn test_crdt_undo_count() {
let mut doc = CrdtDocument::new();
for i in 0..5 {
let rect = Rectangle::new(Point::new(i as f64 * 10.0, 0.0), 50.0, 50.0);
doc.add_shape(&Shape::Rectangle(rect)).expect("Failed to add shape");
}
assert!(doc.undo_count() > 0);
assert_eq!(doc.redo_count(), 0);
}
}