1use crate::{Edge, FlowError, Graph, NodeId, Position, Result};
7
8fn create_edge_with_defaults<E: Default>(
10 id: impl Into<crate::EdgeId>,
11 source: impl Into<NodeId>,
12 target: impl Into<NodeId>,
13) -> Edge<E> {
14 Edge::new(id, source, target, E::default())
15}
16
17#[derive(Debug, Clone)]
19pub struct PreviewEdge {
20 pub source_node: NodeId,
21 pub source_handle: Option<String>,
22 pub start_position: Position,
23 pub end_position: Position,
24}
25
26#[derive(Debug, Clone)]
28pub struct ConnectionFeedback {
29 pub is_valid: bool,
30 pub can_connect: bool,
31 pub message: Option<String>,
32}
33
34#[derive(Debug)]
36pub struct EdgeCreator {
37 preview_edge: Option<PreviewEdge>,
39 is_creating: bool,
41}
42
43impl EdgeCreator {
44 pub fn new() -> Self {
46 Self {
47 preview_edge: None,
48 is_creating: false,
49 }
50 }
51
52 pub fn is_creating_edge(&self) -> bool {
54 self.is_creating
55 }
56
57 fn validate_basic_connection(&self, source_node: &NodeId, target_node: &NodeId) -> Result<()> {
59 if source_node == target_node {
61 return Err(FlowError::SelfConnection);
62 }
63
64 Ok(())
65 }
66
67 fn validate_handle_compatibility<N, E>(
69 &self,
70 graph: &Graph<N, E>,
71 source_node_ref: &crate::Node<N>,
72 target_node_ref: &crate::Node<N>,
73 source_handle: &str,
74 target_handle: &str,
75 source_node_id: &NodeId,
76 ) -> Result<()> {
77 let source_handle_ref = source_node_ref
79 .get_handle(&source_handle.into())
80 .ok_or_else(|| FlowError::handle_not_found(source_handle))?;
81
82 let target_handle_ref = target_node_ref
83 .get_handle(&target_handle.into())
84 .ok_or_else(|| FlowError::handle_not_found(target_handle))?;
85
86 if !source_handle_ref.can_connect_to(target_handle_ref) {
88 return Err(FlowError::invalid_connection(format!(
89 "Incompatible handles: {} cannot connect to {}",
90 source_handle, target_handle
91 )));
92 }
93
94 if let Some(limit) = source_handle_ref.connection_limit {
96 let current_connections = graph
97 .edges()
98 .filter(|edge| {
99 &edge.source == source_node_id
100 && edge.source_handle.as_deref() == Some(source_handle)
101 })
102 .count();
103
104 if current_connections >= limit {
105 return Err(FlowError::connection_limit_exceeded(
106 source_handle,
107 current_connections,
108 limit,
109 ));
110 }
111 }
112
113 if let Some(limit) = target_handle_ref.connection_limit {
115 let target_node_id: NodeId = target_node_ref.id.clone();
116 let current_connections = graph
117 .edges()
118 .filter(|edge| {
119 edge.target == target_node_id
120 && edge.target_handle.as_deref() == Some(target_handle)
121 })
122 .count();
123
124 if current_connections >= limit {
125 return Err(FlowError::connection_limit_exceeded(
126 target_handle,
127 current_connections,
128 limit,
129 ));
130 }
131 }
132
133 Ok(())
134 }
135
136 pub fn get_preview_edge(&self) -> Option<&PreviewEdge> {
138 self.preview_edge.as_ref()
139 }
140
141 pub fn start_edge_creation<N, E>(
143 &mut self,
144 graph: &Graph<N, E>,
145 source_node: &str,
146 source_handle: &str,
147 start_position: Position,
148 ) -> Result<()> {
149 let node_id: NodeId = source_node.into();
150
151 if graph.get_node(&node_id).is_none() {
153 return Err(FlowError::node_not_found(source_node));
154 }
155
156 self.preview_edge = Some(PreviewEdge {
158 source_node: node_id,
159 source_handle: Some(source_handle.to_string()),
160 start_position,
161 end_position: start_position,
162 });
163 self.is_creating = true;
164
165 Ok(())
166 }
167
168 pub fn update_edge_preview(&mut self, end_position: Position) {
170 if let Some(preview) = &mut self.preview_edge {
171 preview.end_position = end_position;
172 }
173 }
174
175 pub fn complete_edge_creation<N, E>(
177 &mut self,
178 graph: &mut Graph<N, E>,
179 target_node: &str,
180 target_handle: Option<&str>,
181 _end_position: Position,
182 ) -> Result<()>
183 where
184 N: Clone,
185 E: Clone + Default,
186 {
187 let preview = self
188 .preview_edge
189 .take()
190 .ok_or_else(|| FlowError::InvalidOperation {
191 message: "No edge creation in progress".to_string(),
192 })?;
193
194 let target_node_id: NodeId = target_node.into();
195
196 if graph.get_node(&target_node_id).is_none() {
198 return Err(FlowError::node_not_found(target_node));
199 }
200
201 self.validate_basic_connection(&preview.source_node, &target_node_id)?;
203
204 let source_node_ref = graph.get_node(&preview.source_node)
206 .ok_or_else(|| FlowError::node_not_found(preview.source_node.as_str()))?;
207 let target_node_ref = graph.get_node(&target_node_id)
208 .ok_or_else(|| FlowError::node_not_found(target_node_id.as_str()))?;
209
210 if let (Some(source_handle), Some(target_handle)) = (&preview.source_handle, target_handle)
212 {
213 self.validate_handle_compatibility(
214 graph,
215 source_node_ref,
216 target_node_ref,
217 source_handle,
218 target_handle,
219 &preview.source_node,
220 )?;
221 }
222
223 let edge_id = format!("edge_{}_to_{}", preview.source_node.as_str(), target_node);
225 let mut edge = create_edge_with_defaults::<E>(
226 edge_id,
227 preview.source_node.as_str(),
228 target_node_id.as_str(),
229 );
230
231 if let Some(source_handle) = preview.source_handle {
233 edge = edge.with_source_handle(source_handle);
234 }
235 if let Some(target_handle) = target_handle {
236 edge = edge.with_target_handle(target_handle.to_string());
237 }
238
239 graph.add_edge(edge)?;
240 self.is_creating = false;
241
242 Ok(())
243 }
244
245 pub fn cancel_edge_creation(&mut self) {
247 self.preview_edge = None;
248 self.is_creating = false;
249 }
250
251 pub fn get_drop_target<N, E>(
253 &self,
254 graph: &Graph<N, E>,
255 position: Position,
256 tolerance: f64,
257 ) -> Option<(NodeId, Option<String>)> {
258 for node in graph.nodes() {
260 for handle in node.handles() {
261 let handle_pos = node.position + handle.position.to_position();
262 let distance = ((position.x - handle_pos.x).powi(2)
263 + (position.y - handle_pos.y).powi(2))
264 .sqrt();
265
266 if distance <= tolerance {
267 return Some((node.id.clone(), Some(handle.id.as_str().to_string())));
268 }
269 }
270 }
271
272 for node in graph.nodes() {
274 let node_bounds = crate::types::Rect::new(
275 node.position.x,
276 node.position.y,
277 node.size.width,
278 node.size.height,
279 );
280
281 if node_bounds.contains_point(position) {
282 return Some((node.id.clone(), None));
283 }
284 }
285
286 None
287 }
288
289 pub fn get_connection_feedback<N, E>(
291 &self,
292 _graph: &Graph<N, E>,
293 target_node: &str,
294 _target_handle: Option<&str>,
295 ) -> ConnectionFeedback {
296 let preview = match &self.preview_edge {
297 Some(preview) => preview,
298 None => {
299 return ConnectionFeedback {
300 is_valid: false,
301 can_connect: false,
302 message: Some("No edge creation in progress".to_string()),
303 };
304 }
305 };
306
307 let target_node_id: NodeId = target_node.into();
308
309 if preview.source_node == target_node_id {
311 return ConnectionFeedback {
312 is_valid: false,
313 can_connect: false,
314 message: Some("Cannot connect to self".to_string()),
315 };
316 }
317
318 ConnectionFeedback {
320 is_valid: true,
321 can_connect: true,
322 message: None,
323 }
324 }
325}
326
327impl Default for EdgeCreator {
328 fn default() -> Self {
329 Self::new()
330 }
331}