grafeo_core/graph/lpg/
node.rs1use arcstr::ArcStr;
7use grafeo_common::types::{EpochId, NodeId, PropertyKey, PropertyMap, Value};
8use serde::{Deserialize, Serialize};
9use smallvec::SmallVec;
10
11#[derive(Debug, Clone)]
30pub struct Node {
31 pub id: NodeId,
33 pub labels: SmallVec<[ArcStr; 2]>,
35 pub properties: PropertyMap,
37}
38
39impl Node {
40 #[must_use]
42 pub fn new(id: NodeId) -> Self {
43 Self {
44 id,
45 labels: SmallVec::new(),
46 properties: PropertyMap::new(),
47 }
48 }
49
50 #[must_use]
52 pub fn with_labels(id: NodeId, labels: impl IntoIterator<Item = impl Into<ArcStr>>) -> Self {
53 Self {
54 id,
55 labels: labels.into_iter().map(Into::into).collect(),
56 properties: PropertyMap::new(),
57 }
58 }
59
60 pub fn add_label(&mut self, label: impl Into<ArcStr>) {
62 let label = label.into();
63 if !self.labels.iter().any(|l| l.as_str() == label.as_str()) {
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_str() == 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_str() == 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 #[must_use]
102 pub fn properties_as_btree(&self) -> std::collections::BTreeMap<PropertyKey, Value> {
103 self.properties.to_btree_map()
104 }
105}
106
107#[repr(C)]
115#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
116pub struct NodeRecord {
117 pub id: NodeId,
119 pub epoch: EpochId,
121 pub props_offset: u32,
123 pub label_count: u16,
125 pub(crate) _reserved: u16,
127 pub props_count: u16,
129 pub flags: NodeFlags,
131 pub(crate) _padding: u32,
133}
134
135impl NodeRecord {
136 pub const FLAG_DELETED: u16 = 1 << 0;
138 pub const FLAG_HAS_VERSION: u16 = 1 << 1;
140
141 #[must_use]
143 pub const fn new(id: NodeId, epoch: EpochId) -> Self {
144 Self {
145 id,
146 label_count: 0,
147 _reserved: 0,
148 props_offset: 0,
149 props_count: 0,
150 flags: NodeFlags(0),
151 epoch,
152 _padding: 0,
153 }
154 }
155
156 #[must_use]
158 pub const fn is_deleted(&self) -> bool {
159 self.flags.contains(Self::FLAG_DELETED)
160 }
161
162 pub fn set_deleted(&mut self, deleted: bool) {
164 if deleted {
165 self.flags.set(Self::FLAG_DELETED);
166 } else {
167 self.flags.clear(Self::FLAG_DELETED);
168 }
169 }
170
171 #[must_use]
173 pub const fn label_count(&self) -> u16 {
174 self.label_count
175 }
176
177 pub fn set_label_count(&mut self, count: u16) {
179 self.label_count = count;
180 }
181}
182
183#[repr(transparent)]
187#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
188pub struct NodeFlags(pub u16);
189
190impl NodeFlags {
191 #[must_use]
193 pub const fn contains(&self, flag: u16) -> bool {
194 (self.0 & flag) != 0
195 }
196
197 pub fn set(&mut self, flag: u16) {
199 self.0 |= flag;
200 }
201
202 pub fn clear(&mut self, flag: u16) {
204 self.0 &= !flag;
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_node_record_size() {
214 assert_eq!(std::mem::size_of::<NodeRecord>(), 32);
216 }
217
218 #[test]
219 fn test_node_labels() {
220 let mut node = Node::new(NodeId::new(1));
221
222 node.add_label("Person");
223 assert!(node.has_label("Person"));
224 assert!(!node.has_label("Animal"));
225
226 node.add_label("Employee");
227 assert!(node.has_label("Employee"));
228
229 node.add_label("Person");
231 assert_eq!(node.labels.len(), 2);
232
233 assert!(node.remove_label("Person"));
235 assert!(!node.has_label("Person"));
236 assert!(!node.remove_label("NotExists"));
237 }
238
239 #[test]
240 fn test_node_properties() {
241 let mut node = Node::new(NodeId::new(1));
242
243 node.set_property("name", "Alix");
244 node.set_property("age", 30i64);
245
246 assert_eq!(
247 node.get_property("name").and_then(|v| v.as_str()),
248 Some("Alix")
249 );
250 assert_eq!(
251 node.get_property("age").and_then(|v| v.as_int64()),
252 Some(30)
253 );
254 assert!(node.get_property("missing").is_none());
255
256 let removed = node.remove_property("name");
257 assert!(removed.is_some());
258 assert!(node.get_property("name").is_none());
259 }
260
261 #[test]
262 fn test_node_record_flags() {
263 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
264
265 assert!(!record.is_deleted());
266 record.set_deleted(true);
267 assert!(record.is_deleted());
268 record.set_deleted(false);
269 assert!(!record.is_deleted());
270 }
271
272 #[test]
273 fn test_node_record_label_count() {
274 let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
275
276 assert_eq!(record.label_count(), 0);
277 record.set_label_count(5);
278 assert_eq!(record.label_count(), 5);
279
280 record.set_label_count(1000);
282 assert_eq!(record.label_count(), 1000);
283 }
284}