use arcstr::ArcStr;
use grafeo_common::types::{EpochId, NodeId, PropertyKey, PropertyMap, Value};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
#[derive(Debug, Clone)]
pub struct Node {
pub id: NodeId,
pub labels: SmallVec<[ArcStr; 2]>,
pub properties: PropertyMap,
}
impl Node {
#[must_use]
pub fn new(id: NodeId) -> Self {
Self {
id,
labels: SmallVec::new(),
properties: PropertyMap::new(),
}
}
#[must_use]
pub fn with_labels(id: NodeId, labels: impl IntoIterator<Item = impl Into<ArcStr>>) -> Self {
Self {
id,
labels: labels.into_iter().map(Into::into).collect(),
properties: PropertyMap::new(),
}
}
pub fn add_label(&mut self, label: impl Into<ArcStr>) {
let label = label.into();
if !self.labels.iter().any(|l| l.as_str() == label.as_str()) {
self.labels.push(label);
}
}
pub fn remove_label(&mut self, label: &str) -> bool {
if let Some(pos) = self.labels.iter().position(|l| l.as_str() == label) {
self.labels.remove(pos);
true
} else {
false
}
}
#[must_use]
pub fn has_label(&self, label: &str) -> bool {
self.labels.iter().any(|l| l.as_str() == label)
}
pub fn set_property(&mut self, key: impl Into<PropertyKey>, value: impl Into<Value>) {
self.properties.insert(key.into(), value.into());
}
#[must_use]
pub fn get_property(&self, key: &str) -> Option<&Value> {
self.properties.get(&PropertyKey::new(key))
}
pub fn remove_property(&mut self, key: &str) -> Option<Value> {
self.properties.remove(&PropertyKey::new(key))
}
#[must_use]
pub fn properties_as_btree(&self) -> std::collections::BTreeMap<PropertyKey, Value> {
self.properties.to_btree_map()
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct NodeRecord {
pub id: NodeId,
pub epoch: EpochId,
pub props_offset: u32,
pub label_count: u16,
pub(crate) _reserved: u16,
pub props_count: u16,
pub flags: NodeFlags,
pub(crate) _padding: u32,
}
impl NodeRecord {
pub const FLAG_DELETED: u16 = 1 << 0;
pub const FLAG_HAS_VERSION: u16 = 1 << 1;
#[must_use]
pub const fn new(id: NodeId, epoch: EpochId) -> Self {
Self {
id,
label_count: 0,
_reserved: 0,
props_offset: 0,
props_count: 0,
flags: NodeFlags(0),
epoch,
_padding: 0,
}
}
#[must_use]
pub const fn is_deleted(&self) -> bool {
self.flags.contains(Self::FLAG_DELETED)
}
pub fn set_deleted(&mut self, deleted: bool) {
if deleted {
self.flags.set(Self::FLAG_DELETED);
} else {
self.flags.clear(Self::FLAG_DELETED);
}
}
#[must_use]
pub const fn label_count(&self) -> u16 {
self.label_count
}
pub fn set_label_count(&mut self, count: u16) {
self.label_count = count;
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct NodeFlags(pub u16);
impl NodeFlags {
#[must_use]
pub const fn contains(&self, flag: u16) -> bool {
(self.0 & flag) != 0
}
pub fn set(&mut self, flag: u16) {
self.0 |= flag;
}
pub fn clear(&mut self, flag: u16) {
self.0 &= !flag;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_record_size() {
assert_eq!(std::mem::size_of::<NodeRecord>(), 32);
}
#[test]
fn test_node_labels() {
let mut node = Node::new(NodeId::new(1));
node.add_label("Person");
assert!(node.has_label("Person"));
assert!(!node.has_label("Animal"));
node.add_label("Employee");
assert!(node.has_label("Employee"));
node.add_label("Person");
assert_eq!(node.labels.len(), 2);
assert!(node.remove_label("Person"));
assert!(!node.has_label("Person"));
assert!(!node.remove_label("NotExists"));
}
#[test]
fn test_node_properties() {
let mut node = Node::new(NodeId::new(1));
node.set_property("name", "Alix");
node.set_property("age", 30i64);
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alix")
);
assert_eq!(
node.get_property("age").and_then(|v| v.as_int64()),
Some(30)
);
assert!(node.get_property("missing").is_none());
let removed = node.remove_property("name");
assert!(removed.is_some());
assert!(node.get_property("name").is_none());
}
#[test]
fn test_node_record_flags() {
let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
assert!(!record.is_deleted());
record.set_deleted(true);
assert!(record.is_deleted());
record.set_deleted(false);
assert!(!record.is_deleted());
}
#[test]
fn test_node_record_label_count() {
let mut record = NodeRecord::new(NodeId::new(1), EpochId::INITIAL);
assert_eq!(record.label_count(), 0);
record.set_label_count(5);
assert_eq!(record.label_count(), 5);
record.set_label_count(1000);
assert_eq!(record.label_count(), 1000);
}
}