ruvector_graph/
node.rs

1//! Node implementation
2
3use crate::types::{Label, NodeId, Properties, PropertyValue};
4use bincode::{Decode, Encode};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
10pub struct Node {
11    pub id: NodeId,
12    pub labels: Vec<Label>,
13    pub properties: Properties,
14}
15
16impl Node {
17    pub fn new(id: NodeId, labels: Vec<Label>, properties: Properties) -> Self {
18        Self {
19            id,
20            labels,
21            properties,
22        }
23    }
24
25    /// Check if node has a specific label
26    pub fn has_label(&self, label_name: &str) -> bool {
27        self.labels.iter().any(|l| l.name == label_name)
28    }
29
30    /// Get a property value by key
31    pub fn get_property(&self, key: &str) -> Option<&PropertyValue> {
32        self.properties.get(key)
33    }
34
35    /// Set a property value
36    pub fn set_property(&mut self, key: impl Into<String>, value: PropertyValue) {
37        self.properties.insert(key.into(), value);
38    }
39
40    /// Add a label to the node
41    pub fn add_label(&mut self, label: impl Into<String>) {
42        self.labels.push(Label::new(label));
43    }
44
45    /// Remove a label from the node
46    pub fn remove_label(&mut self, label_name: &str) -> bool {
47        let len_before = self.labels.len();
48        self.labels.retain(|l| l.name != label_name);
49        self.labels.len() < len_before
50    }
51}
52
53/// Builder for constructing Node instances
54#[derive(Debug, Clone, Default)]
55pub struct NodeBuilder {
56    id: Option<NodeId>,
57    labels: Vec<Label>,
58    properties: Properties,
59}
60
61impl NodeBuilder {
62    /// Create a new node builder
63    pub fn new() -> Self {
64        Self::default()
65    }
66
67    /// Set the node ID
68    pub fn id(mut self, id: impl Into<String>) -> Self {
69        self.id = Some(id.into());
70        self
71    }
72
73    /// Add a label to the node
74    pub fn label(mut self, label: impl Into<String>) -> Self {
75        self.labels.push(Label::new(label));
76        self
77    }
78
79    /// Add multiple labels to the node
80    pub fn labels(mut self, labels: impl IntoIterator<Item = impl Into<String>>) -> Self {
81        for label in labels {
82            self.labels.push(Label::new(label));
83        }
84        self
85    }
86
87    /// Add a property to the node
88    pub fn property<V: Into<PropertyValue>>(mut self, key: impl Into<String>, value: V) -> Self {
89        self.properties.insert(key.into(), value.into());
90        self
91    }
92
93    /// Add multiple properties to the node
94    pub fn properties(mut self, props: Properties) -> Self {
95        self.properties.extend(props);
96        self
97    }
98
99    /// Build the node
100    pub fn build(self) -> Node {
101        Node {
102            id: self.id.unwrap_or_else(|| Uuid::new_v4().to_string()),
103            labels: self.labels,
104            properties: self.properties,
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_node_builder() {
115        let node = NodeBuilder::new()
116            .label("Person")
117            .property("name", "Alice")
118            .property("age", 30i64)
119            .build();
120
121        assert!(node.has_label("Person"));
122        assert!(!node.has_label("Organization"));
123        assert_eq!(
124            node.get_property("name"),
125            Some(&PropertyValue::String("Alice".to_string()))
126        );
127    }
128
129    #[test]
130    fn test_node_has_label() {
131        let node = NodeBuilder::new().label("Person").label("Employee").build();
132
133        assert!(node.has_label("Person"));
134        assert!(node.has_label("Employee"));
135        assert!(!node.has_label("Company"));
136    }
137
138    #[test]
139    fn test_node_modify_labels() {
140        let mut node = NodeBuilder::new().label("Person").build();
141
142        node.add_label("Employee");
143        assert!(node.has_label("Employee"));
144
145        let removed = node.remove_label("Person");
146        assert!(removed);
147        assert!(!node.has_label("Person"));
148    }
149}