1use crate::types::{NodeId, Properties, PropertyValue};
6use bincode::{Decode, Encode};
7use serde::{Deserialize, Serialize};
8use std::collections::HashSet;
9use uuid::Uuid;
10
11pub type HyperedgeId = String;
13
14#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
16pub struct Hyperedge {
17 pub id: HyperedgeId,
19 pub nodes: Vec<NodeId>,
21 pub edge_type: String,
23 pub description: Option<String>,
25 pub properties: Properties,
27 pub confidence: f32,
29}
30
31impl Hyperedge {
32 pub fn new<S: Into<String>>(nodes: Vec<NodeId>, edge_type: S) -> Self {
34 Self {
35 id: Uuid::new_v4().to_string(),
36 nodes,
37 edge_type: edge_type.into(),
38 description: None,
39 properties: Properties::new(),
40 confidence: 1.0,
41 }
42 }
43
44 pub fn with_id<S: Into<String>>(id: HyperedgeId, nodes: Vec<NodeId>, edge_type: S) -> Self {
46 Self {
47 id,
48 nodes,
49 edge_type: edge_type.into(),
50 description: None,
51 properties: Properties::new(),
52 confidence: 1.0,
53 }
54 }
55
56 pub fn order(&self) -> usize {
58 self.nodes.len()
59 }
60
61 pub fn contains_node(&self, node_id: &NodeId) -> bool {
63 self.nodes.contains(node_id)
64 }
65
66 pub fn contains_all_nodes(&self, node_ids: &[NodeId]) -> bool {
68 node_ids.iter().all(|id| self.contains_node(id))
69 }
70
71 pub fn contains_any_node(&self, node_ids: &[NodeId]) -> bool {
73 node_ids.iter().any(|id| self.contains_node(id))
74 }
75
76 pub fn unique_nodes(&self) -> HashSet<&NodeId> {
78 self.nodes.iter().collect()
79 }
80
81 pub fn set_description<S: Into<String>>(&mut self, description: S) -> &mut Self {
83 self.description = Some(description.into());
84 self
85 }
86
87 pub fn set_confidence(&mut self, confidence: f32) -> &mut Self {
89 self.confidence = confidence.clamp(0.0, 1.0);
90 self
91 }
92
93 pub fn set_property<K, V>(&mut self, key: K, value: V) -> &mut Self
95 where
96 K: Into<String>,
97 V: Into<PropertyValue>,
98 {
99 self.properties.insert(key.into(), value.into());
100 self
101 }
102
103 pub fn get_property(&self, key: &str) -> Option<&PropertyValue> {
105 self.properties.get(key)
106 }
107
108 pub fn remove_property(&mut self, key: &str) -> Option<PropertyValue> {
110 self.properties.remove(key)
111 }
112
113 pub fn has_property(&self, key: &str) -> bool {
115 self.properties.contains_key(key)
116 }
117
118 pub fn property_keys(&self) -> Vec<&String> {
120 self.properties.keys().collect()
121 }
122
123 pub fn clear_properties(&mut self) {
125 self.properties.clear();
126 }
127
128 pub fn property_count(&self) -> usize {
130 self.properties.len()
131 }
132}
133
134pub struct HyperedgeBuilder {
136 hyperedge: Hyperedge,
137}
138
139impl HyperedgeBuilder {
140 pub fn new<S: Into<String>>(nodes: Vec<NodeId>, edge_type: S) -> Self {
142 Self {
143 hyperedge: Hyperedge::new(nodes, edge_type),
144 }
145 }
146
147 pub fn with_id<S: Into<String>>(id: HyperedgeId, nodes: Vec<NodeId>, edge_type: S) -> Self {
149 Self {
150 hyperedge: Hyperedge::with_id(id, nodes, edge_type),
151 }
152 }
153
154 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
156 self.hyperedge.set_description(description);
157 self
158 }
159
160 pub fn confidence(mut self, confidence: f32) -> Self {
162 self.hyperedge.set_confidence(confidence);
163 self
164 }
165
166 pub fn property<K, V>(mut self, key: K, value: V) -> Self
168 where
169 K: Into<String>,
170 V: Into<PropertyValue>,
171 {
172 self.hyperedge.set_property(key, value);
173 self
174 }
175
176 pub fn build(self) -> Hyperedge {
178 self.hyperedge
179 }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
184pub struct HyperedgeWithRoles {
185 pub hyperedge: Hyperedge,
187 pub roles: std::collections::HashMap<NodeId, String>,
189}
190
191impl HyperedgeWithRoles {
192 pub fn new(hyperedge: Hyperedge) -> Self {
194 Self {
195 hyperedge,
196 roles: std::collections::HashMap::new(),
197 }
198 }
199
200 pub fn assign_role<S: Into<String>>(&mut self, node_id: NodeId, role: S) -> &mut Self {
202 self.roles.insert(node_id, role.into());
203 self
204 }
205
206 pub fn get_role(&self, node_id: &NodeId) -> Option<&String> {
208 self.roles.get(node_id)
209 }
210
211 pub fn nodes_with_role(&self, role: &str) -> Vec<&NodeId> {
213 self.roles
214 .iter()
215 .filter(|(_, r)| r.as_str() == role)
216 .map(|(id, _)| id)
217 .collect()
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_hyperedge_creation() {
227 let nodes = vec![
228 "node1".to_string(),
229 "node2".to_string(),
230 "node3".to_string(),
231 ];
232 let hedge = Hyperedge::new(nodes, "MEETING");
233
234 assert!(!hedge.id.is_empty());
235 assert_eq!(hedge.order(), 3);
236 assert_eq!(hedge.edge_type, "MEETING");
237 assert_eq!(hedge.confidence, 1.0);
238 }
239
240 #[test]
241 fn test_hyperedge_contains() {
242 let nodes = vec![
243 "node1".to_string(),
244 "node2".to_string(),
245 "node3".to_string(),
246 ];
247 let hedge = Hyperedge::new(nodes, "MEETING");
248
249 assert!(hedge.contains_node(&"node1".to_string()));
250 assert!(hedge.contains_node(&"node2".to_string()));
251 assert!(!hedge.contains_node(&"node4".to_string()));
252
253 assert!(hedge.contains_all_nodes(&["node1".to_string(), "node2".to_string()]));
254 assert!(!hedge.contains_all_nodes(&["node1".to_string(), "node4".to_string()]));
255
256 assert!(hedge.contains_any_node(&["node1".to_string(), "node4".to_string()]));
257 assert!(!hedge.contains_any_node(&["node4".to_string(), "node5".to_string()]));
258 }
259
260 #[test]
261 fn test_hyperedge_builder() {
262 let nodes = vec!["node1".to_string(), "node2".to_string()];
263 let hedge = HyperedgeBuilder::new(nodes, "COLLABORATION")
264 .description("Team collaboration on project X")
265 .confidence(0.95)
266 .property("project", "X")
267 .property("duration", 30i64)
268 .build();
269
270 assert_eq!(hedge.edge_type, "COLLABORATION");
271 assert_eq!(hedge.confidence, 0.95);
272 assert!(hedge.description.is_some());
273 assert_eq!(
274 hedge.get_property("project"),
275 Some(&PropertyValue::String("X".to_string()))
276 );
277 }
278
279 #[test]
280 fn test_hyperedge_with_roles() {
281 let nodes = vec![
282 "alice".to_string(),
283 "bob".to_string(),
284 "charlie".to_string(),
285 ];
286 let hedge = Hyperedge::new(nodes, "MEETING");
287
288 let mut hedge_with_roles = HyperedgeWithRoles::new(hedge);
289 hedge_with_roles.assign_role("alice".to_string(), "organizer");
290 hedge_with_roles.assign_role("bob".to_string(), "participant");
291 hedge_with_roles.assign_role("charlie".to_string(), "participant");
292
293 assert_eq!(
294 hedge_with_roles.get_role(&"alice".to_string()),
295 Some(&"organizer".to_string())
296 );
297
298 let participants = hedge_with_roles.nodes_with_role("participant");
299 assert_eq!(participants.len(), 2);
300 }
301
302 #[test]
303 fn test_unique_nodes() {
304 let nodes = vec![
305 "node1".to_string(),
306 "node2".to_string(),
307 "node1".to_string(), ];
309 let hedge = Hyperedge::new(nodes, "TEST");
310
311 let unique = hedge.unique_nodes();
312 assert_eq!(unique.len(), 2);
313 }
314}