grafeo_core/graph/lpg/
node.rs1use std::collections::BTreeMap;
7use std::sync::Arc;
8
9use grafeo_common::types::{EpochId, NodeId, PropertyKey, Value};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone)]
31pub struct Node {
32 pub id: NodeId,
34 pub labels: Vec<Arc<str>>,
36 pub properties: BTreeMap<PropertyKey, Value>,
38}
39
40impl Node {
41 #[must_use]
43 pub fn new(id: NodeId) -> Self {
44 Self {
45 id,
46 labels: Vec::new(),
47 properties: BTreeMap::new(),
48 }
49 }
50
51 #[must_use]
53 pub fn with_labels(id: NodeId, labels: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
54 Self {
55 id,
56 labels: labels.into_iter().map(Into::into).collect(),
57 properties: BTreeMap::new(),
58 }
59 }
60
61 pub fn add_label(&mut self, label: impl Into<Arc<str>>) {
63 let label = label.into();
64 if !self.labels.iter().any(|l| l.as_ref() == label.as_ref()) {
65 self.labels.push(label);
66 }
67 }
68
69 pub fn remove_label(&mut self, label: &str) -> bool {
71 if let Some(pos) = self.labels.iter().position(|l| l.as_ref() == label) {
72 self.labels.remove(pos);
73 true
74 } else {
75 false
76 }
77 }
78
79 #[must_use]
81 pub fn has_label(&self, label: &str) -> bool {
82 self.labels.iter().any(|l| l.as_ref() == label)
83 }
84
85 pub fn set_property(&mut self, key: impl Into<PropertyKey>, value: impl Into<Value>) {
87 self.properties.insert(key.into(), value.into());
88 }
89
90 #[must_use]
92 pub fn get_property(&self, key: &str) -> Option<&Value> {
93 self.properties.get(&PropertyKey::new(key))
94 }
95
96 pub fn remove_property(&mut self, key: &str) -> Option<Value> {
98 self.properties.remove(&PropertyKey::new(key))
99 }
100}
101
102#[repr(C)]
110#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
111pub struct NodeRecord {
112 pub id: NodeId,
114 pub epoch: EpochId,
116 pub props_offset: u32,
118 pub label_count: u16,
120 pub _reserved: u16,
122 pub props_count: u16,
124 pub flags: NodeFlags,
126 pub _padding: u32,
128}
129
130impl NodeRecord {
131 pub const FLAG_DELETED: u16 = 1 << 0;
133 pub const FLAG_HAS_VERSION: u16 = 1 << 1;
135
136 #[must_use]
138 pub const fn new(id: NodeId, epoch: EpochId) -> Self {
139 Self {
140 id,
141 label_count: 0,
142 _reserved: 0,
143 props_offset: 0,
144 props_count: 0,
145 flags: NodeFlags(0),
146 epoch,
147 _padding: 0,
148 }
149 }
150
151 #[must_use]
153 pub const fn is_deleted(&self) -> bool {
154 self.flags.contains(Self::FLAG_DELETED)
155 }
156
157 pub fn set_deleted(&mut self, deleted: bool) {
159 if deleted {
160 self.flags.set(Self::FLAG_DELETED);
161 } else {
162 self.flags.clear(Self::FLAG_DELETED);
163 }
164 }
165
166 #[must_use]
168 pub const fn label_count(&self) -> u16 {
169 self.label_count
170 }
171
172 pub fn set_label_count(&mut self, count: u16) {
174 self.label_count = count;
175 }
176}
177
178#[repr(transparent)]
182#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
183pub struct NodeFlags(pub u16);
184
185impl NodeFlags {
186 #[must_use]
188 pub const fn contains(&self, flag: u16) -> bool {
189 (self.0 & flag) != 0
190 }
191
192 pub fn set(&mut self, flag: u16) {
194 self.0 |= flag;
195 }
196
197 pub fn clear(&mut self, flag: u16) {
199 self.0 &= !flag;
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_node_record_size() {
209 assert_eq!(std::mem::size_of::<NodeRecord>(), 32);
211 }
212
213 #[test]
214 fn test_node_labels() {
215 let mut node = Node::new(NodeId::new(1));
216
217 node.add_label("Person");
218 assert!(node.has_label("Person"));
219 assert!(!node.has_label("Animal"));
220
221 node.add_label("Employee");
222 assert!(node.has_label("Employee"));
223
224 node.add_label("Person");
226 assert_eq!(node.labels.len(), 2);
227
228 assert!(node.remove_label("Person"));
230 assert!(!node.has_label("Person"));
231 assert!(!node.remove_label("NotExists"));
232 }
233
234 #[test]
235 fn test_node_properties() {
236 let mut node = Node::new(NodeId::new(1));
237
238 node.set_property("name", "Alice");
239 node.set_property("age", 30i64);
240
241 assert_eq!(
242 node.get_property("name").and_then(|v| v.as_str()),
243 Some("Alice")
244 );
245 assert_eq!(
246 node.get_property("age").and_then(|v| v.as_int64()),
247 Some(30)
248 );
249 assert!(node.get_property("missing").is_none());
250
251 let removed = node.remove_property("name");
252 assert!(removed.is_some());
253 assert!(node.get_property("name").is_none());
254 }
255
256 #[test]
257 fn test_node_record_flags() {
258 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
259
260 assert!(!record.is_deleted());
261 record.set_deleted(true);
262 assert!(record.is_deleted());
263 record.set_deleted(false);
264 assert!(!record.is_deleted());
265 }
266
267 #[test]
268 fn test_node_record_label_count() {
269 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
270
271 assert_eq!(record.label_count(), 0);
272 record.set_label_count(5);
273 assert_eq!(record.label_count(), 5);
274
275 record.set_label_count(1000);
277 assert_eq!(record.label_count(), 1000);
278 }
279}