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