1use serde::{Deserialize, Serialize};
2
3use crate::element::Element;
4use crate::point::ViewState;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct Document {
8 pub id: String,
9 pub version: u32,
10 pub name: String,
11 pub elements: Vec<Element>,
12 #[serde(default)]
13 pub view: ViewState,
14 pub created_at: String,
15 pub modified_at: String,
16}
17
18impl Document {
19 pub fn new(name: String) -> Self {
20 let now = chrono::Utc::now().to_rfc3339();
21 Self {
22 id: uuid::Uuid::new_v4().to_string(),
23 version: 1,
24 name,
25 elements: vec![],
26 view: ViewState::default(),
27 created_at: now.clone(),
28 modified_at: now,
29 }
30 }
31
32 pub fn add_element(&mut self, element: Element) {
33 self.touch();
34 self.elements.push(element);
35 }
36
37 pub fn remove_element(&mut self, id: &str) -> Option<Element> {
38 if let Some(idx) = self.elements.iter().position(|e| e.id() == id) {
39 self.touch();
40 Some(self.elements.remove(idx))
41 } else {
42 None
43 }
44 }
45
46 pub fn get_element(&self, id: &str) -> Option<&Element> {
47 self.elements.iter().find(|e| e.id() == id)
48 }
49
50 pub fn get_element_mut(&mut self, id: &str) -> Option<&mut Element> {
51 self.elements.iter_mut().find(|e| e.id() == id)
52 }
53
54 fn touch(&mut self) {
55 self.modified_at = chrono::Utc::now().to_rfc3339();
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::element::{Element, ShapeElement};
63
64 #[test]
65 fn test_new_document() {
66 let doc = Document::new("my drawing".to_string());
67 assert_eq!(doc.name, "my drawing");
68 assert_eq!(doc.version, 1);
69 assert!(doc.elements.is_empty());
70 assert!(!doc.id.is_empty());
71 }
72
73 #[test]
74 fn test_add_element() {
75 let mut doc = Document::new("test".to_string());
76 let elem = Element::Rectangle(ShapeElement::new("r1".to_string(), 0.0, 0.0, 10.0, 10.0));
77 doc.add_element(elem);
78 assert_eq!(doc.elements.len(), 1);
79 assert_eq!(doc.elements[0].id(), "r1");
80 }
81
82 #[test]
83 fn test_remove_element() {
84 let mut doc = Document::new("test".to_string());
85 let elem = Element::Rectangle(ShapeElement::new("r1".to_string(), 0.0, 0.0, 10.0, 10.0));
86 let modified_before = doc.modified_at.clone();
87 doc.add_element(elem);
88
89 let removed = doc.remove_element("r1");
91 assert!(removed.is_some());
92 assert_eq!(removed.unwrap().id(), "r1");
93 assert!(doc.elements.is_empty());
94
95 let modified_after_remove = doc.modified_at.clone();
97 let removed = doc.remove_element("nonexistent");
98 assert!(removed.is_none());
99 assert_eq!(doc.modified_at, modified_after_remove);
101 let _ = modified_before;
102 }
103
104 #[test]
105 fn test_json_roundtrip() {
106 use crate::element::{FreeDrawElement, LineElement, TextElement};
107 use crate::point::Point;
108
109 let mut doc = Document::new("roundtrip test".to_string());
110 doc.add_element(Element::Rectangle(ShapeElement::new(
111 "r1".to_string(),
112 10.0,
113 20.0,
114 100.0,
115 50.0,
116 )));
117 doc.add_element(Element::Ellipse(ShapeElement::new(
118 "e1".to_string(),
119 0.0,
120 0.0,
121 80.0,
122 60.0,
123 )));
124 doc.add_element(Element::Line(LineElement::new(
125 "l1".to_string(),
126 5.0,
127 5.0,
128 vec![Point::new(0.0, 0.0), Point::new(50.0, 50.0)],
129 )));
130 doc.add_element(Element::FreeDraw(FreeDrawElement::new(
131 "fd1".to_string(),
132 1.0,
133 2.0,
134 vec![
135 Point::new(0.0, 0.0),
136 Point::new(3.0, 4.0),
137 Point::new(6.0, 1.0),
138 ],
139 )));
140 doc.add_element(Element::Text(TextElement::new(
141 "t1".to_string(),
142 0.0,
143 0.0,
144 "hello\nworld".to_string(),
145 )));
146
147 let json = serde_json::to_string_pretty(&doc).unwrap();
148 let deserialized: Document = serde_json::from_str(&json).unwrap();
149 assert_eq!(doc, deserialized);
150 }
151
152 #[test]
153 fn test_get_element() {
154 let mut doc = Document::new("test".to_string());
155 doc.add_element(Element::Rectangle(ShapeElement::new(
156 "r1".to_string(),
157 5.0,
158 10.0,
159 20.0,
160 30.0,
161 )));
162 assert!(doc.get_element("r1").is_some());
163 assert!(doc.get_element("nonexistent").is_none());
164 }
165}