kanban_core/graph/
edge.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub enum EdgeDirection {
8 Directed,
10 Bidirectional,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Edge<E> {
20 pub source: Uuid,
22 pub target: Uuid,
24 pub edge_type: E,
26 pub direction: EdgeDirection,
28 pub weight: Option<f32>,
30 pub created_at: DateTime<Utc>,
32 pub archived_at: Option<DateTime<Utc>>,
34}
35
36impl<E> Edge<E> {
37 pub fn new(source: Uuid, target: Uuid, edge_type: E, direction: EdgeDirection) -> Self {
39 Self {
40 source,
41 target,
42 edge_type,
43 direction,
44 weight: None,
45 created_at: Utc::now(),
46 archived_at: None,
47 }
48 }
49
50 pub fn is_archived(&self) -> bool {
52 self.archived_at.is_some()
53 }
54
55 pub fn is_active(&self) -> bool {
57 self.archived_at.is_none()
58 }
59
60 pub fn archive(&mut self) {
62 if self.archived_at.is_none() {
63 self.archived_at = Some(Utc::now());
64 }
65 }
66
67 pub fn unarchive(&mut self) {
69 self.archived_at = None;
70 }
71
72 pub fn involves(&self, node_id: Uuid) -> bool {
74 self.source == node_id || self.target == node_id
75 }
76
77 pub fn connects(&self, node_a: Uuid, node_b: Uuid) -> bool {
79 match self.direction {
80 EdgeDirection::Directed => self.source == node_a && self.target == node_b,
81 EdgeDirection::Bidirectional => {
82 (self.source == node_a && self.target == node_b)
83 || (self.source == node_b && self.target == node_a)
84 }
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
94 enum TestEdgeType {
95 TypeA,
96 TypeB,
97 }
98
99 #[test]
100 fn test_edge_creation() {
101 let source = Uuid::new_v4();
102 let target = Uuid::new_v4();
103 let edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
104
105 assert_eq!(edge.source, source);
106 assert_eq!(edge.target, target);
107 assert!(edge.is_active());
108 assert!(!edge.is_archived());
109 }
110
111 #[test]
112 fn test_edge_archive() {
113 let source = Uuid::new_v4();
114 let target = Uuid::new_v4();
115 let mut edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
116
117 edge.archive();
118 assert!(edge.is_archived());
119 assert!(!edge.is_active());
120
121 edge.unarchive();
122 assert!(edge.is_active());
123 assert!(!edge.is_archived());
124 }
125
126 #[test]
127 fn test_edge_involves() {
128 let source = Uuid::new_v4();
129 let target = Uuid::new_v4();
130 let other = Uuid::new_v4();
131 let edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
132
133 assert!(edge.involves(source));
134 assert!(edge.involves(target));
135 assert!(!edge.involves(other));
136 }
137
138 #[test]
139 fn test_edge_connects_directed() {
140 let source = Uuid::new_v4();
141 let target = Uuid::new_v4();
142 let edge = Edge::new(source, target, TestEdgeType::TypeA, EdgeDirection::Directed);
143
144 assert!(edge.connects(source, target));
145 assert!(!edge.connects(target, source));
146 }
147
148 #[test]
149 fn test_edge_connects_bidirectional() {
150 let node_a = Uuid::new_v4();
151 let node_b = Uuid::new_v4();
152 let edge = Edge::new(
153 node_a,
154 node_b,
155 TestEdgeType::TypeA,
156 EdgeDirection::Bidirectional,
157 );
158
159 assert!(edge.connects(node_a, node_b));
160 assert!(edge.connects(node_b, node_a));
161 }
162}