flow_rs_core/graph/
edge.rs

1//! Edge data structure and builder pattern
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use crate::error::{FlowError, Result};
7use crate::types::{EdgeId, NodeId};
8
9/// Edge connecting two nodes
10#[derive(Debug, Clone, PartialEq)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Edge<T = ()> {
13    pub id: EdgeId,
14    pub source: NodeId,
15    pub target: NodeId,
16    pub data: T,
17
18    // Optional properties
19    pub source_handle: Option<String>,
20    pub target_handle: Option<String>,
21    pub edge_type: Option<String>,
22    pub selected: bool,
23    pub animated: bool,
24    pub hidden: bool,
25    pub selectable: bool,
26    pub deletable: bool,
27    pub z_index: Option<i32>,
28    pub label: Option<String>,
29}
30
31impl<T> Edge<T> {
32    /// Create a new edge
33    pub fn new(
34        id: impl Into<EdgeId>,
35        source: impl Into<NodeId>,
36        target: impl Into<NodeId>,
37        data: T,
38    ) -> Self {
39        Self {
40            id: id.into(),
41            source: source.into(),
42            target: target.into(),
43            data,
44            source_handle: None,
45            target_handle: None,
46            edge_type: None,
47            selected: false,
48            animated: false,
49            hidden: false,
50            selectable: true,
51            deletable: true,
52            z_index: None,
53            label: None,
54        }
55    }
56
57    /// Create a builder for fluent construction
58    pub fn builder() -> EdgeBuilder<T> {
59        EdgeBuilder::new()
60    }
61
62    /// Set selection state
63    pub fn set_selected(&mut self, selected: bool) {
64        self.selected = selected;
65    }
66
67    /// Check if edge connects the given nodes
68    pub fn connects(&self, source_id: &NodeId, target_id: &NodeId) -> bool {
69        &self.source == source_id && &self.target == target_id
70    }
71
72    /// Check if edge is connected to the given node
73    pub fn is_connected_to(&self, node_id: &NodeId) -> bool {
74        &self.source == node_id || &self.target == node_id
75    }
76
77    /// Convert to a different data type
78    pub fn map_data<U>(self, f: impl FnOnce(T) -> U) -> Edge<U> {
79        Edge {
80            id: self.id,
81            source: self.source,
82            target: self.target,
83            data: f(self.data),
84            source_handle: self.source_handle,
85            target_handle: self.target_handle,
86            edge_type: self.edge_type,
87            selected: self.selected,
88            animated: self.animated,
89            hidden: self.hidden,
90            selectable: self.selectable,
91            deletable: self.deletable,
92            z_index: self.z_index,
93            label: self.label,
94        }
95    }
96
97    /// Set source handle
98    pub fn with_source_handle(mut self, handle_id: impl Into<String>) -> Self {
99        self.source_handle = Some(handle_id.into());
100        self
101    }
102
103    /// Set target handle
104    pub fn with_target_handle(mut self, handle_id: impl Into<String>) -> Self {
105        self.target_handle = Some(handle_id.into());
106        self
107    }
108}
109
110impl Edge<()> {
111    /// Create a simple edge with default data
112    pub fn simple(
113        id: impl Into<EdgeId>,
114        source: impl Into<NodeId>,
115        target: impl Into<NodeId>,
116    ) -> Self {
117        Self::new(id, source, target, ())
118    }
119}
120
121impl<T: Default> Edge<T> {
122    /// Create an edge with default data
123    pub fn with_default_data(
124        id: impl Into<EdgeId>,
125        source: impl Into<NodeId>,
126        target: impl Into<NodeId>,
127    ) -> Self {
128        Self::new(id, source, target, T::default())
129    }
130}
131
132/// Builder for fluent edge construction
133#[derive(Debug)]
134pub struct EdgeBuilder<T> {
135    id: Option<EdgeId>,
136    source: Option<NodeId>,
137    target: Option<NodeId>,
138    data: Option<T>,
139    source_handle: Option<String>,
140    target_handle: Option<String>,
141    edge_type: Option<String>,
142    selected: bool,
143    animated: bool,
144    hidden: bool,
145    selectable: bool,
146    deletable: bool,
147    z_index: Option<i32>,
148    label: Option<String>,
149}
150
151impl<T> EdgeBuilder<T> {
152    /// Create a new edge builder
153    pub fn new() -> Self {
154        Self {
155            id: None,
156            source: None,
157            target: None,
158            data: None,
159            source_handle: None,
160            target_handle: None,
161            edge_type: None,
162            selected: false,
163            animated: false,
164            hidden: false,
165            selectable: true,
166            deletable: true,
167            z_index: None,
168            label: None,
169        }
170    }
171
172    /// Set edge ID
173    pub fn id(mut self, id: impl Into<EdgeId>) -> Self {
174        self.id = Some(id.into());
175        self
176    }
177
178    /// Connect two nodes
179    pub fn connect(mut self, source: impl Into<NodeId>, target: impl Into<NodeId>) -> Self {
180        self.source = Some(source.into());
181        self.target = Some(target.into());
182        self
183    }
184
185    /// Connect specific handles
186    pub fn connect_handles(
187        mut self,
188        source: impl Into<NodeId>,
189        source_handle: impl Into<String>,
190        target: impl Into<NodeId>,
191        target_handle: impl Into<String>,
192    ) -> Self {
193        self.source = Some(source.into());
194        self.target = Some(target.into());
195        self.source_handle = Some(source_handle.into());
196        self.target_handle = Some(target_handle.into());
197        self
198    }
199
200    /// Set data
201    pub fn data(mut self, data: T) -> Self {
202        self.data = Some(data);
203        self
204    }
205
206    /// Set edge type
207    pub fn edge_type(mut self, edge_type: impl Into<String>) -> Self {
208        self.edge_type = Some(edge_type.into());
209        self
210    }
211
212    /// Set animated flag
213    pub fn animated(mut self, animated: bool) -> Self {
214        self.animated = animated;
215        self
216    }
217
218    /// Set label
219    pub fn label(mut self, label: impl Into<String>) -> Self {
220        self.label = Some(label.into());
221        self
222    }
223
224    /// Build the edge
225    pub fn build(self) -> Result<Edge<T>>
226    where
227        T: Default,
228    {
229        let id = self.id.unwrap_or_else(EdgeId::generate);
230        let source = self
231            .source
232            .ok_or_else(|| FlowError::invalid_connection("Source node not specified"))?;
233        let target = self
234            .target
235            .ok_or_else(|| FlowError::invalid_connection("Target node not specified"))?;
236        let data = self.data.unwrap_or_default();
237
238        if source == target {
239            return Err(FlowError::SelfConnection);
240        }
241
242        Ok(Edge {
243            id,
244            source,
245            target,
246            data,
247            source_handle: self.source_handle,
248            target_handle: self.target_handle,
249            edge_type: self.edge_type,
250            selected: self.selected,
251            animated: self.animated,
252            hidden: self.hidden,
253            selectable: self.selectable,
254            deletable: self.deletable,
255            z_index: self.z_index,
256            label: self.label,
257        })
258    }
259
260    /// Build the edge with specific data
261    pub fn build_with_data(self, data: T) -> Result<Edge<T>> {
262        let id = self.id.unwrap_or_else(EdgeId::generate);
263        let source = self
264            .source
265            .ok_or_else(|| FlowError::invalid_connection("Source node not specified"))?;
266        let target = self
267            .target
268            .ok_or_else(|| FlowError::invalid_connection("Target node not specified"))?;
269
270        if source == target {
271            return Err(FlowError::SelfConnection);
272        }
273
274        Ok(Edge {
275            id,
276            source,
277            target,
278            data,
279            source_handle: self.source_handle,
280            target_handle: self.target_handle,
281            edge_type: self.edge_type,
282            selected: self.selected,
283            animated: self.animated,
284            hidden: self.hidden,
285            selectable: self.selectable,
286            deletable: self.deletable,
287            z_index: self.z_index,
288            label: self.label,
289        })
290    }
291}
292
293impl<T> Default for EdgeBuilder<T> {
294    fn default() -> Self {
295        Self::new()
296    }
297}