1use super::property::{PropertyMap, PropertyValue};
41use super::types::{Label, NodeId};
42use serde::{Deserialize, Serialize};
43use std::collections::HashSet;
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Node {
54 pub id: NodeId,
56
57 pub version: u64,
59
60 pub labels: HashSet<Label>,
62
63 pub properties: PropertyMap,
65
66 pub created_at: i64,
68
69 pub updated_at: i64,
71}
72
73impl Node {
74 pub fn new(id: NodeId, label: impl Into<Label>) -> Self {
76 let now = chrono::Utc::now().timestamp_millis();
77 let mut labels = HashSet::new();
78 labels.insert(label.into());
79
80 Node {
81 id,
82 version: 1,
83 labels,
84 properties: PropertyMap::new(),
85 created_at: now,
86 updated_at: now,
87 }
88 }
89
90 pub fn new_with_labels(id: NodeId, labels: Vec<Label>) -> Self {
92 let now = chrono::Utc::now().timestamp_millis();
93 let label_set: HashSet<Label> = labels.into_iter().collect();
94
95 Node {
96 id,
97 version: 1,
98 labels: label_set,
99 properties: PropertyMap::new(),
100 created_at: now,
101 updated_at: now,
102 }
103 }
104
105 pub fn new_with_properties(id: NodeId, labels: Vec<Label>, properties: PropertyMap) -> Self {
107 let now = chrono::Utc::now().timestamp_millis();
108 let label_set: HashSet<Label> = labels.into_iter().collect();
109
110 Node {
111 id,
112 version: 1,
113 labels: label_set,
114 properties,
115 created_at: now,
116 updated_at: now,
117 }
118 }
119
120 pub fn add_label(&mut self, label: impl Into<Label>) {
122 self.labels.insert(label.into());
123 self.update_timestamp();
124 }
125
126 pub fn remove_label(&mut self, label: &Label) -> bool {
128 let removed = self.labels.remove(label);
129 if removed {
130 self.update_timestamp();
131 }
132 removed
133 }
134
135 pub fn has_label(&self, label: &Label) -> bool {
137 self.labels.contains(label)
138 }
139
140 pub fn get_labels(&self) -> Vec<&Label> {
142 self.labels.iter().collect()
143 }
144
145 pub fn set_property(
147 &mut self,
148 key: impl Into<String>,
149 value: impl Into<PropertyValue>,
150 ) -> Option<PropertyValue> {
151 let old = self.properties.insert(key.into(), value.into());
152 self.update_timestamp();
153 old
154 }
155
156 pub fn get_property(&self, key: &str) -> Option<&PropertyValue> {
158 self.properties.get(key)
159 }
160
161 pub fn remove_property(&mut self, key: &str) -> Option<PropertyValue> {
163 let removed = self.properties.remove(key);
164 if removed.is_some() {
165 self.update_timestamp();
166 }
167 removed
168 }
169
170 pub fn has_property(&self, key: &str) -> bool {
172 self.properties.contains_key(key)
173 }
174
175 fn update_timestamp(&mut self) {
177 self.updated_at = chrono::Utc::now().timestamp_millis();
178 }
179
180 pub fn property_count(&self) -> usize {
182 self.properties.len()
183 }
184
185 pub fn label_count(&self) -> usize {
187 self.labels.len()
188 }
189}
190
191mod chrono {
193 pub struct Utc;
194 impl Utc {
195 pub fn now() -> DateTime {
196 DateTime
197 }
198 }
199 pub struct DateTime;
200 impl DateTime {
201 pub fn timestamp_millis(&self) -> i64 {
202 std::time::SystemTime::now()
204 .duration_since(std::time::UNIX_EPOCH)
205 .unwrap()
206 .as_millis() as i64
207 }
208 }
209}
210
211impl PartialEq for Node {
212 fn eq(&self, other: &Self) -> bool {
213 self.id == other.id
214 }
215}
216
217impl Eq for Node {}
218
219impl std::hash::Hash for Node {
220 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
221 self.id.hash(state);
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_create_node_single_label() {
231 let node = Node::new(NodeId::new(1), "Person");
233 assert_eq!(node.id, NodeId::new(1));
234 assert_eq!(node.labels.len(), 1);
235 assert!(node.has_label(&Label::new("Person")));
236 }
237
238 #[test]
239 fn test_create_node_multiple_labels() {
240 let labels = vec![Label::new("Person"), Label::new("Employee")];
242 let node = Node::new_with_labels(NodeId::new(2), labels);
243
244 assert_eq!(node.label_count(), 2);
245 assert!(node.has_label(&Label::new("Person")));
246 assert!(node.has_label(&Label::new("Employee")));
247 }
248
249 #[test]
250 fn test_add_remove_labels() {
251 let mut node = Node::new(NodeId::new(3), "Person");
252
253 node.add_label("Employee");
255 assert_eq!(node.label_count(), 2);
256 assert!(node.has_label(&Label::new("Employee")));
257
258 let removed = node.remove_label(&Label::new("Person"));
260 assert!(removed);
261 assert_eq!(node.label_count(), 1);
262 assert!(!node.has_label(&Label::new("Person")));
263 }
264
265 #[test]
266 fn test_node_properties() {
267 let mut node = Node::new(NodeId::new(4), "Person");
269
270 node.set_property("name", "Alice");
272 node.set_property("age", 30i64);
273 node.set_property("active", true);
274
275 assert_eq!(
277 node.get_property("name").unwrap().as_string(),
278 Some("Alice")
279 );
280 assert_eq!(node.get_property("age").unwrap().as_integer(), Some(30));
281 assert_eq!(
282 node.get_property("active").unwrap().as_boolean(),
283 Some(true)
284 );
285 assert_eq!(node.property_count(), 3);
286
287 let removed = node.remove_property("age");
289 assert!(removed.is_some());
290 assert_eq!(node.property_count(), 2);
291 assert!(!node.has_property("age"));
292 }
293
294 #[test]
295 fn test_node_with_properties() {
296 let mut props = PropertyMap::new();
298 props.insert("name".to_string(), "Bob".into());
299 props.insert("age".to_string(), 25i64.into());
300 props.insert("score".to_string(), 95.5.into());
301
302 let node = Node::new_with_properties(NodeId::new(5), vec![Label::new("Student")], props);
303
304 assert_eq!(node.property_count(), 3);
305 assert_eq!(node.get_property("name").unwrap().as_string(), Some("Bob"));
306 assert_eq!(node.get_property("age").unwrap().as_integer(), Some(25));
307 assert_eq!(node.get_property("score").unwrap().as_float(), Some(95.5));
308 }
309
310 #[test]
311 fn test_node_timestamps() {
312 let node = Node::new(NodeId::new(6), "Test");
313 assert!(node.created_at > 0);
314 assert_eq!(node.created_at, node.updated_at);
315
316 std::thread::sleep(std::time::Duration::from_millis(10));
317 let mut node2 = node.clone();
318 node2.set_property("key", "value");
319
320 assert!(node2.updated_at > node.updated_at);
321 }
322
323 #[test]
324 fn test_node_equality() {
325 let node1 = Node::new(NodeId::new(7), "Person");
326 let node2 = Node::new(NodeId::new(7), "Person");
327 let node3 = Node::new(NodeId::new(8), "Person");
328
329 assert_eq!(node1, node2); assert_ne!(node1, node3); }
332}