1use super::property::{PropertyMap, PropertyValue};
41use super::types::{EdgeId, EdgeType, NodeId};
42use serde::{Deserialize, Serialize};
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct Edge {
55 pub id: EdgeId,
57
58 pub version: u64,
60
61 pub source: NodeId,
63
64 pub target: NodeId,
66
67 pub edge_type: EdgeType,
69
70 pub properties: PropertyMap,
72
73 pub created_at: i64,
75}
76
77impl Edge {
78 pub fn new(id: EdgeId, source: NodeId, target: NodeId, edge_type: impl Into<EdgeType>) -> Self {
80 let now = Self::current_timestamp();
81
82 Edge {
83 id,
84 version: 1,
85 source,
86 target,
87 edge_type: edge_type.into(),
88 properties: PropertyMap::new(),
89 created_at: now,
90 }
91 }
92
93 pub fn new_with_properties(
95 id: EdgeId,
96 source: NodeId,
97 target: NodeId,
98 edge_type: impl Into<EdgeType>,
99 properties: PropertyMap,
100 ) -> Self {
101 let now = Self::current_timestamp();
102
103 Edge {
104 id,
105 version: 1,
106 source,
107 target,
108 edge_type: edge_type.into(),
109 properties,
110 created_at: now,
111 }
112 }
113
114 pub fn set_property(&mut self, key: impl Into<String>, value: impl Into<PropertyValue>) {
116 self.properties.insert(key.into(), value.into());
117 }
118
119 pub fn get_property(&self, key: &str) -> Option<&PropertyValue> {
121 self.properties.get(key)
122 }
123
124 pub fn remove_property(&mut self, key: &str) -> Option<PropertyValue> {
126 self.properties.remove(key)
127 }
128
129 pub fn has_property(&self, key: &str) -> bool {
131 self.properties.contains_key(key)
132 }
133
134 pub fn property_count(&self) -> usize {
136 self.properties.len()
137 }
138
139 pub fn connects(&self, node1: NodeId, node2: NodeId) -> bool {
141 (self.source == node1 && self.target == node2)
142 || (self.source == node2 && self.target == node1)
143 }
144
145 pub fn starts_from(&self, node: NodeId) -> bool {
147 self.source == node
148 }
149
150 pub fn ends_at(&self, node: NodeId) -> bool {
152 self.target == node
153 }
154
155 fn current_timestamp() -> i64 {
157 std::time::SystemTime::now()
158 .duration_since(std::time::UNIX_EPOCH)
159 .unwrap()
160 .as_millis() as i64
161 }
162}
163
164impl PartialEq for Edge {
165 fn eq(&self, other: &Self) -> bool {
166 self.id == other.id
167 }
168}
169
170impl Eq for Edge {}
171
172impl std::hash::Hash for Edge {
173 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
174 self.id.hash(state);
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_create_edge() {
184 let edge = Edge::new(EdgeId::new(1), NodeId::new(1), NodeId::new(2), "KNOWS");
187
188 assert_eq!(edge.id, EdgeId::new(1));
189 assert_eq!(edge.source, NodeId::new(1));
190 assert_eq!(edge.target, NodeId::new(2));
191 assert_eq!(edge.edge_type.as_str(), "KNOWS");
192 }
193
194 #[test]
195 fn test_edge_direction() {
196 let edge = Edge::new(EdgeId::new(2), NodeId::new(10), NodeId::new(20), "FOLLOWS");
198
199 assert!(edge.starts_from(NodeId::new(10)));
200 assert!(edge.ends_at(NodeId::new(20)));
201 assert!(!edge.starts_from(NodeId::new(20)));
202 assert!(!edge.ends_at(NodeId::new(10)));
203 }
204
205 #[test]
206 fn test_edge_properties() {
207 let mut edge = Edge::new(EdgeId::new(3), NodeId::new(1), NodeId::new(2), "KNOWS");
209
210 edge.set_property("since", 2020i64);
212 edge.set_property("strength", 0.95);
213 edge.set_property("verified", true);
214
215 assert_eq!(edge.get_property("since").unwrap().as_integer(), Some(2020));
217 assert_eq!(
218 edge.get_property("strength").unwrap().as_float(),
219 Some(0.95)
220 );
221 assert_eq!(
222 edge.get_property("verified").unwrap().as_boolean(),
223 Some(true)
224 );
225 assert_eq!(edge.property_count(), 3);
226 }
227
228 #[test]
229 fn test_edge_with_properties() {
230 let mut props = PropertyMap::new();
232 props.insert("weight".to_string(), 10i64.into());
233 props.insert("label".to_string(), "important".into());
234
235 let edge = Edge::new_with_properties(
236 EdgeId::new(4),
237 NodeId::new(5),
238 NodeId::new(6),
239 "RELATED_TO",
240 props,
241 );
242
243 assert_eq!(edge.property_count(), 2);
244 assert_eq!(edge.get_property("weight").unwrap().as_integer(), Some(10));
245 assert_eq!(
246 edge.get_property("label").unwrap().as_string(),
247 Some("important")
248 );
249 }
250
251 #[test]
252 fn test_multiple_edges_between_nodes() {
253 let node1 = NodeId::new(100);
255 let node2 = NodeId::new(200);
256
257 let edge1 = Edge::new(EdgeId::new(1), node1, node2, "KNOWS");
258 let edge2 = Edge::new(EdgeId::new(2), node1, node2, "WORKS_WITH");
259 let edge3 = Edge::new(EdgeId::new(3), node1, node2, "KNOWS");
260
261 assert_ne!(edge1, edge2);
263 assert_ne!(edge1, edge3);
264 assert_ne!(edge2, edge3);
265
266 assert!(edge1.connects(node1, node2));
268 assert!(edge2.connects(node1, node2));
269 assert!(edge3.connects(node1, node2));
270
271 assert_eq!(edge1.edge_type, EdgeType::new("KNOWS"));
273 assert_eq!(edge2.edge_type, EdgeType::new("WORKS_WITH"));
274 }
275
276 #[test]
277 fn test_edge_connects() {
278 let edge = Edge::new(EdgeId::new(5), NodeId::new(10), NodeId::new(20), "LINKS");
279
280 assert!(edge.connects(NodeId::new(10), NodeId::new(20)));
281 assert!(edge.connects(NodeId::new(20), NodeId::new(10))); assert!(!edge.connects(NodeId::new(10), NodeId::new(30)));
283 }
284
285 #[test]
286 fn test_remove_property() {
287 let mut edge = Edge::new(EdgeId::new(6), NodeId::new(1), NodeId::new(2), "TEST");
288
289 edge.set_property("temp", "value");
290 assert!(edge.has_property("temp"));
291
292 let removed = edge.remove_property("temp");
293 assert!(removed.is_some());
294 assert!(!edge.has_property("temp"));
295 assert_eq!(edge.property_count(), 0);
296 }
297}