graphos_core/graph/lpg/
node.rs1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use graphos_common::types::{EpochId, NodeId, PropertyKey, Value};
7
8#[derive(Debug, Clone)]
12pub struct Node {
13 pub id: NodeId,
15 pub labels: Vec<Arc<str>>,
17 pub properties: BTreeMap<PropertyKey, Value>,
19}
20
21impl Node {
22 #[must_use]
24 pub fn new(id: NodeId) -> Self {
25 Self {
26 id,
27 labels: Vec::new(),
28 properties: BTreeMap::new(),
29 }
30 }
31
32 #[must_use]
34 pub fn with_labels(id: NodeId, labels: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
35 Self {
36 id,
37 labels: labels.into_iter().map(Into::into).collect(),
38 properties: BTreeMap::new(),
39 }
40 }
41
42 pub fn add_label(&mut self, label: impl Into<Arc<str>>) {
44 let label = label.into();
45 if !self.labels.iter().any(|l| l.as_ref() == label.as_ref()) {
46 self.labels.push(label);
47 }
48 }
49
50 pub fn remove_label(&mut self, label: &str) -> bool {
52 if let Some(pos) = self.labels.iter().position(|l| l.as_ref() == label) {
53 self.labels.remove(pos);
54 true
55 } else {
56 false
57 }
58 }
59
60 #[must_use]
62 pub fn has_label(&self, label: &str) -> bool {
63 self.labels.iter().any(|l| l.as_ref() == label)
64 }
65
66 pub fn set_property(&mut self, key: impl Into<PropertyKey>, value: impl Into<Value>) {
68 self.properties.insert(key.into(), value.into());
69 }
70
71 #[must_use]
73 pub fn get_property(&self, key: &str) -> Option<&Value> {
74 self.properties.get(&PropertyKey::new(key))
75 }
76
77 pub fn remove_property(&mut self, key: &str) -> Option<Value> {
79 self.properties.remove(&PropertyKey::new(key))
80 }
81}
82
83#[repr(C)]
89#[derive(Debug, Clone, Copy)]
90pub struct NodeRecord {
91 pub id: NodeId,
93 pub epoch: EpochId,
95 pub props_offset: u32,
97 pub label_count: u16,
99 pub _reserved: u16,
101 pub props_count: u16,
103 pub flags: NodeFlags,
105 pub _padding: u32,
107}
108
109impl NodeRecord {
110 pub const FLAG_DELETED: u16 = 1 << 0;
112 pub const FLAG_HAS_VERSION: u16 = 1 << 1;
114
115 #[must_use]
117 pub const fn new(id: NodeId, epoch: EpochId) -> Self {
118 Self {
119 id,
120 label_count: 0,
121 _reserved: 0,
122 props_offset: 0,
123 props_count: 0,
124 flags: NodeFlags(0),
125 epoch,
126 _padding: 0,
127 }
128 }
129
130 #[must_use]
132 pub const fn is_deleted(&self) -> bool {
133 self.flags.contains(Self::FLAG_DELETED)
134 }
135
136 pub fn set_deleted(&mut self, deleted: bool) {
138 if deleted {
139 self.flags.set(Self::FLAG_DELETED);
140 } else {
141 self.flags.clear(Self::FLAG_DELETED);
142 }
143 }
144
145 #[must_use]
147 pub const fn label_count(&self) -> u16 {
148 self.label_count
149 }
150
151 pub fn set_label_count(&mut self, count: u16) {
153 self.label_count = count;
154 }
155}
156
157#[repr(transparent)]
159#[derive(Debug, Clone, Copy, Default)]
160pub struct NodeFlags(pub u16);
161
162impl NodeFlags {
163 #[must_use]
165 pub const fn contains(&self, flag: u16) -> bool {
166 (self.0 & flag) != 0
167 }
168
169 pub fn set(&mut self, flag: u16) {
171 self.0 |= flag;
172 }
173
174 pub fn clear(&mut self, flag: u16) {
176 self.0 &= !flag;
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_node_record_size() {
186 assert_eq!(std::mem::size_of::<NodeRecord>(), 32);
188 }
189
190 #[test]
191 fn test_node_labels() {
192 let mut node = Node::new(NodeId::new(1));
193
194 node.add_label("Person");
195 assert!(node.has_label("Person"));
196 assert!(!node.has_label("Animal"));
197
198 node.add_label("Employee");
199 assert!(node.has_label("Employee"));
200
201 node.add_label("Person");
203 assert_eq!(node.labels.len(), 2);
204
205 assert!(node.remove_label("Person"));
207 assert!(!node.has_label("Person"));
208 assert!(!node.remove_label("NotExists"));
209 }
210
211 #[test]
212 fn test_node_properties() {
213 let mut node = Node::new(NodeId::new(1));
214
215 node.set_property("name", "Alice");
216 node.set_property("age", 30i64);
217
218 assert_eq!(
219 node.get_property("name").and_then(|v| v.as_str()),
220 Some("Alice")
221 );
222 assert_eq!(
223 node.get_property("age").and_then(|v| v.as_int64()),
224 Some(30)
225 );
226 assert!(node.get_property("missing").is_none());
227
228 let removed = node.remove_property("name");
229 assert!(removed.is_some());
230 assert!(node.get_property("name").is_none());
231 }
232
233 #[test]
234 fn test_node_record_flags() {
235 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
236
237 assert!(!record.is_deleted());
238 record.set_deleted(true);
239 assert!(record.is_deleted());
240 record.set_deleted(false);
241 assert!(!record.is_deleted());
242 }
243
244 #[test]
245 fn test_node_record_label_count() {
246 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
247
248 assert_eq!(record.label_count(), 0);
249 record.set_label_count(5);
250 assert_eq!(record.label_count(), 5);
251
252 record.set_label_count(1000);
254 assert_eq!(record.label_count(), 1000);
255 }
256}