1use uuid::Uuid;
8
9use super::desc::BlockStaticDesc;
10use super::{Block, BlockProps};
11use crate::base::input::InputProps;
12use crate::base::link::{BaseLink, Link, LinkState};
13use crate::base::output::Output;
14
15pub trait BlockConnect: BlockStaticDesc {
17 fn connect_output(
24 &mut self,
25 output_name: &str,
26 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
27 ) -> Result<Uuid, &'static str>;
28
29 fn connect_input(
36 &mut self,
37 input_name: &str,
38 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
39 ) -> Result<Uuid, &'static str>;
40
41 fn disconnect_output(
47 &mut self,
48 source_output_name: &str,
49 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
50 ) -> Result<(), &'static str>;
51
52 fn disconnect_input(
57 &mut self,
58 source_input_name: &str,
59 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
60 ) -> Result<(), &'static str>;
61}
62
63impl<T: Block + ?Sized> BlockConnect for T {
68 fn connect_output(
69 &mut self,
70 source_output_name: &str,
71 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
72 ) -> Result<Uuid, &'static str> {
73 let mut outputs = self.outputs_mut();
74 let source_output = if let Some(out) = outputs
75 .iter_mut()
76 .find(|output| output.desc().name == source_output_name)
77 {
78 out
79 } else {
80 return Err("Output not found");
81 };
82
83 connect_output(*source_output, target_input)
84 }
85
86 fn connect_input(
87 &mut self,
88 source_input_name: &str,
89 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
90 ) -> Result<Uuid, &'static str> {
91 let mut inputs = self.inputs_mut();
92 let source_input = if let Some(input) = inputs
93 .iter_mut()
94 .find(|input| input.name() == source_input_name)
95 {
96 *input as &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>
97 } else {
98 return Err("Input not found");
99 };
100 connect_input(source_input, target_input)
101 }
102
103 fn disconnect_output(
104 &mut self,
105 source_output_name: &str,
106 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
107 ) -> Result<(), &'static str> {
108 let mut outputs = self.outputs_mut();
109 let source_output = if let Some(out) = outputs
110 .iter_mut()
111 .find(|output| output.desc().name == source_output_name)
112 {
113 out
114 } else {
115 return Err("Output not found");
116 };
117
118 disconnect_output(*source_output, target_input)
119 }
120
121 fn disconnect_input(
122 &mut self,
123 source_input_name: &str,
124 target_input: &mut dyn InputProps<Reader = Self::Reader, Writer = Self::Writer>,
125 ) -> Result<(), &'static str> {
126 let mut inputs = self.inputs_mut();
127 let source_input = if let Some(in_) = inputs
128 .iter_mut()
129 .find(|input| input.name() == source_input_name)
130 {
131 in_
132 } else {
133 return Err("Input not found");
134 };
135
136 let link_id = link_id_for_input(source_input.links(), target_input);
137
138 if let Some(id) = link_id {
139 source_input.remove_link_by_id(&id);
140 target_input.decrement_conn();
141 Ok(())
142 } else {
143 Err("No connection found")
144 }
145 }
146}
147
148pub fn connect_output<Reader, Writer: Clone>(
153 source_output: &mut dyn Output<Writer = Writer>,
154 target_input: &mut dyn InputProps<Reader = Reader, Writer = Writer>,
155) -> Result<Uuid, &'static str> {
156 if source_output.links().iter().any(|link| {
158 link.target_block_id() == target_input.block_id()
159 && link.target_input() == target_input.name()
160 }) {
161 return Err("Already connected");
162 }
163
164 let mut link = BaseLink::new(*target_input.block_id(), target_input.name().to_string());
165 let id = link.id;
166
167 link.tx = Some(target_input.writer().clone());
168
169 link.state = LinkState::Connected;
170
171 source_output.add_link(link);
172 target_input.increment_conn();
173
174 Ok(id)
175}
176
177pub fn disconnect_output<Reader, Writer: Clone>(
185 source_output: &mut dyn Output<Writer = Writer>,
186 target_input: &mut dyn InputProps<Reader = Reader, Writer = Writer>,
187) -> Result<(), &'static str> {
188 let link_id = link_id_for_input(source_output.links(), target_input);
189
190 if let Some(id) = link_id {
191 source_output.remove_link_by_id(&id);
192 target_input.decrement_conn();
193 Ok(())
194 } else {
195 Err("No connection found")
196 }
197}
198
199pub fn connect_input<Reader, Writer: Clone>(
204 source_input: &mut dyn InputProps<Reader = Reader, Writer = Writer>,
205 target_input: &mut dyn InputProps<Reader = Reader, Writer = Writer>,
206) -> Result<Uuid, &'static str> {
207 if source_input.block_id() == target_input.block_id() {
208 return Err("Cannot connect to the same block");
209 }
210
211 if source_input.links().iter().any(|link| {
212 link.target_block_id() == target_input.block_id()
213 && link.target_input() == target_input.name()
214 }) {
215 return Err("Already connected");
216 }
217
218 let mut link = BaseLink::new(*target_input.block_id(), target_input.name().to_string());
219 let id = link.id;
220
221 link.tx = Some(target_input.writer().clone());
222
223 link.state = LinkState::Connected;
224
225 source_input.add_link(link);
226 target_input.increment_conn();
227
228 Ok(id)
229}
230
231pub fn disconnect_input<I: InputProps + ?Sized>(
238 source_input: &mut I,
239 target_input: &mut I,
240) -> Result<(), &'static str> {
241 let link_id = link_id_for_input(source_input.links(), target_input);
242
243 if let Some(id) = link_id {
244 source_input.remove_link_by_id(&id);
245 target_input.decrement_conn();
246 Ok(())
247 } else {
248 Err("No connection found")
249 }
250}
251
252pub fn disconnect_block<B, F>(block: &mut B, mut decrement_target_input: F)
257where
258 B: BlockProps + ?Sized,
259 F: FnMut(&Uuid, &str) -> Option<usize>,
260{
261 block
262 .outputs_mut()
263 .iter()
264 .filter(|output| output.is_connected())
265 .for_each(|out| {
266 out.links().iter_mut().for_each(|link| {
267 decrement_target_input(link.target_block_id(), link.target_input());
268 });
269 });
270
271 block
272 .inputs_mut()
273 .iter()
274 .filter(|input| input.has_output())
275 .for_each(|src_input| {
276 src_input.links().iter_mut().for_each(|link| {
277 decrement_target_input(link.target_block_id(), link.target_input());
278 });
279 });
280
281 block.remove_all_links();
282}
283
284pub fn disconnect_link<B, F>(block: &mut B, link_id: &Uuid, mut decrement_target_input: F) -> bool
290where
291 B: BlockProps + ?Sized,
292 F: FnMut(&Uuid, &str) -> Option<usize>,
293{
294 if !block
295 .outputs_mut()
296 .iter()
297 .filter(|output| output.is_connected())
298 .any(|out| {
299 out.links().iter_mut().any(|link| {
300 if link.id() == link_id {
301 decrement_target_input(link.target_block_id(), link.target_input());
302 true
303 } else {
304 false
305 }
306 })
307 })
308 && !block
309 .inputs_mut()
310 .iter()
311 .filter(|input| input.has_output())
312 .any(|src_input| {
313 src_input.links().iter_mut().any(|link| {
314 if link.id() == link_id {
315 decrement_target_input(link.target_block_id(), link.target_input());
316 true
317 } else {
318 false
319 }
320 })
321 })
322 {
323 return false;
324 }
325
326 block.remove_link_by_id(link_id);
327 true
328}
329
330fn link_id_for_input<I: InputProps + ?Sized>(
331 links: Vec<&dyn Link>,
332 target_input: &I,
333) -> Option<Uuid> {
334 let link_id = links
335 .iter()
336 .find(|link| {
337 link.target_input() == target_input.name()
338 && link.target_block_id() == target_input.block_id()
339 })
340 .map(|link| *link.id());
341 link_id
342}
343
344#[cfg(test)]
345mod test {
346
347 use uuid::Uuid;
348
349 use crate::base::{
350 block::{connect::disconnect_block, Block, BlockDesc, BlockProps, BlockState},
351 input::{Input, InputProps},
352 output::Output,
353 };
354
355 use super::BlockConnect;
356
357 use crate::base::block::test_utils::mock::{InputImpl, OutputImpl};
358
359 use libhaystack::val::kind::HaystackKind;
360
361 #[block]
362 #[derive(BlockProps, Debug)]
363 #[category = "test"]
364 struct Block1 {
365 #[input(kind = "Number")]
366 input1: InputImpl,
367 #[output(kind = "Number")]
368 out: OutputImpl,
369 }
370 impl Block for Block1 {
371 async fn execute(&mut self) {
372 todo!()
373 }
374 }
375
376 #[block]
377 #[derive(BlockProps, Debug)]
378 #[category = "test"]
379 struct Block2 {
380 #[input(kind = "Number")]
381 input1: InputImpl,
382 #[output(kind = "Number")]
383 out: OutputImpl,
384 }
385 impl Block for Block2 {
386 async fn execute(&mut self) {
387 todo!()
388 }
389 }
390
391 #[test]
392 fn test_block_out_links() {
393 let mut block1 = Block1::new();
394 let mut block2 = Block2::new();
395
396 assert_eq!(block1.name(), "Block1");
397 assert_eq!(block2.name(), "Block2");
398
399 let input = &mut block2.inputs_mut()[0];
400 block1
401 .connect_output("out", *input)
402 .expect("Could not connect");
403
404 assert!(input.is_connected());
405 assert_eq!(block1.outputs()[0].links().len(), 1);
406
407 assert!(
408 block1.connect_output("out", *input).is_err(),
409 "Should not be able to connect twice"
410 );
411
412 assert!(
413 block1.connect_output("invalid out", *input).is_err(),
414 "Should not be able to connect to invalid output"
415 );
416
417 block1
418 .disconnect_output("out", *input)
419 .expect("Could not disconnect");
420
421 assert!(!input.is_connected());
422 assert_eq!(block1.outputs()[0].links().len(), 0);
423 }
424
425 #[test]
426 fn test_block_input_links() {
427 let mut block1 = Block1::new();
428 let mut block2 = Block2::new();
429
430 let input2 = &mut block2.inputs_mut()[0];
431
432 block1
433 .connect_input("input1", *input2)
434 .expect("Could not connect");
435
436 assert!(
437 block1.connect_input("input1", *input2).is_err(),
438 "Should not be able to connect twice"
439 );
440 assert!(
441 block1.connect_input("invalid input", *input2).is_err(),
442 "Should not be able to connect to invalid input"
443 );
444
445 assert!(block1.input1.has_output());
446 assert!(input2.is_connected());
447 assert_eq!(block1.input1.links().len(), 1);
448 assert_eq!(input2.links().len(), 0);
449
450 block1
451 .disconnect_input("input1", *input2)
452 .expect("Could not disconnect");
453
454 assert!(!block1.input1.is_connected());
455 assert_eq!(block1.outputs()[0].links().len(), 0);
456 }
457
458 #[test]
459 fn test_block_disconnect() {
460 let mut block1 = Block1::new();
461 let mut block2 = Block2::new();
462 {
463 let input2 = &mut block2.inputs_mut()[0];
464
465 block1
466 .connect_input("input1", *input2)
467 .expect("Could not connect");
468
469 assert!(
470 block1.connect_input("input1", *input2).is_err(),
471 "Should not be able to connect twice"
472 );
473 assert!(
474 block1.connect_input("invalid input", *input2).is_err(),
475 "Should not be able to connect to invalid input"
476 );
477
478 assert!(input2.is_connected());
479 assert!(block1.input1.links().len() == 1);
480 }
481
482 let gid = block2.id().clone();
483 let input1 = &mut block2.input1;
484
485 disconnect_block(&mut block1, |id, name| {
486 assert!(*id == gid);
487 assert!(name == "input1");
488 Some(input1.decrement_conn())
489 });
490
491 assert!(!input1.is_connected());
492 assert!(block1.input1.links().len() == 0);
493 }
494}