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