portgraph/view.rs
1//! Abstractions over portgraph representations.
2
3pub mod filter;
4pub mod refs;
5
6mod flat_region;
7mod region;
8mod subgraph;
9
10#[cfg(feature = "petgraph")]
11pub mod petgraph;
12
13use std::collections::HashMap;
14
15use crate::{
16 index::Unsigned, portgraph::PortOperation, Direction, LinkError, NodeIndex, PortIndex,
17 PortOffset,
18};
19
20pub use filter::{FilteredGraph, LinkFilter, NodeFilter, NodeFiltered};
21pub use flat_region::FlatRegion;
22pub use region::Region;
23pub use subgraph::{CopySubgraphError, Subgraph};
24
25/// Core capabilities for querying a graph containing nodes and ports.
26pub trait PortView {
27 /// The base unsigned integer type for port offsets.
28 type PortOffsetBase: Unsigned;
29
30 /// Returns the direction of the `port`.
31 #[must_use]
32 fn port_direction(&self, port: impl Into<PortIndex>) -> Option<Direction>;
33
34 /// Returns the node that the `port` belongs to.
35 #[must_use]
36 fn port_node(&self, port: impl Into<PortIndex>) -> Option<NodeIndex>;
37
38 /// Returns the index of a `port` within its node's port list.
39 #[must_use]
40 fn port_offset(&self, port: impl Into<PortIndex>) -> Option<PortOffset<Self::PortOffsetBase>>;
41
42 /// Returns the port index for a given node and offset.
43 #[must_use]
44 fn port_index(
45 &self,
46 node: NodeIndex,
47 offset: PortOffset<Self::PortOffsetBase>,
48 ) -> Option<PortIndex>;
49
50 /// Iterates over all the ports of the `node` in the given `direction`.
51 #[must_use]
52 fn ports(
53 &self,
54 node: NodeIndex,
55 direction: Direction,
56 ) -> impl Iterator<Item = PortIndex> + Clone;
57
58 /// Iterates over the input and output ports of the `node` in sequence.
59 #[must_use]
60 fn all_ports(&self, node: NodeIndex) -> impl Iterator<Item = PortIndex> + Clone;
61
62 /// Iterates over all the input ports of the `node`.
63 ///
64 /// Shorthand for [`PortView::ports`]`(`[`Direction::Incoming`]`)`.
65 #[must_use]
66 #[inline]
67 fn inputs(&self, node: NodeIndex) -> impl Iterator<Item = PortIndex> + Clone {
68 self.ports(node, Direction::Incoming)
69 }
70
71 /// Iterates over all the output ports of the `node`.
72 ///
73 /// Shorthand for [`PortView::ports`]`(`[`Direction::Outgoing`]`)`.
74 #[must_use]
75 #[inline]
76 fn outputs(&self, node: NodeIndex) -> impl Iterator<Item = PortIndex> + Clone {
77 self.ports(node, Direction::Outgoing)
78 }
79
80 /// Returns the input port at the given offset in the `node`.
81 ///
82 /// Shorthand for [`PortView::port_index`].
83 #[must_use]
84 fn input(&self, node: NodeIndex, offset: usize) -> Option<PortIndex>;
85
86 /// Returns the output port at the given offset in the `node`.
87 ///
88 /// Shorthand for [`PortView::port_index`].
89 #[must_use]
90 fn output(&self, node: NodeIndex, offset: usize) -> Option<PortIndex>;
91
92 /// Returns the number of input ports of the `node`.
93 ///
94 /// Shorthand for [`PortView::num_ports`]`(`[`Direction::Incoming`]`)`..
95 #[must_use]
96 #[inline]
97 fn num_inputs(&self, node: NodeIndex) -> usize {
98 self.num_ports(node, Direction::Incoming)
99 }
100
101 /// Returns the number of output ports of the `node`.
102 ///
103 /// Shorthand for [`PortView::num_ports`]`(`[`Direction::Outgoing`]`)`..
104 #[must_use]
105 #[inline]
106 fn num_outputs(&self, node: NodeIndex) -> usize {
107 self.num_ports(node, Direction::Outgoing)
108 }
109
110 /// Returns the number of ports of the `node` in the given `direction`.
111 #[must_use]
112 fn num_ports(&self, node: NodeIndex, direction: Direction) -> usize;
113
114 /// Iterates over all the port offsets of the `node` in the given `direction`.
115 ///
116 /// # Example
117 ///
118 /// ```
119 /// # use ::portgraph::*;
120 /// let mut graph: PortGraph = PortGraph::new();
121 /// let node = graph.add_node(0, 2);
122 ///
123 /// assert!(graph.links(node, Direction::Incoming).eq([]));
124 /// assert!(graph.port_offsets(node, Direction::Outgoing).eq([PortOffset::new_outgoing(0), PortOffset::new_outgoing(1)]));
125 /// ```
126 #[must_use]
127 fn port_offsets(
128 &self,
129 node: NodeIndex,
130 direction: Direction,
131 ) -> impl Iterator<Item = PortOffset<Self::PortOffsetBase>> + Clone;
132
133 /// Iterates over the input and output port offsets of the `node` in sequence.
134 #[must_use]
135 fn all_port_offsets(
136 &self,
137 node: NodeIndex,
138 ) -> impl Iterator<Item = PortOffset<Self::PortOffsetBase>> + Clone;
139
140 /// Iterates over all the input port offsets of the `node`.
141 ///
142 /// Shorthand for [`PortView::port_offsets`]`(`[`Direction::Incoming`]`)`.
143 #[must_use]
144 #[inline]
145 fn input_offsets(
146 &self,
147 node: NodeIndex,
148 ) -> impl Iterator<Item = PortOffset<Self::PortOffsetBase>> + Clone {
149 self.port_offsets(node, Direction::Incoming)
150 }
151
152 /// Iterates over all the output port offsets of the `node`.
153 ///
154 /// Shorthand for [`PortView::port_offsets`]`(`[`Direction::Outgoing`]`)`.
155 #[must_use]
156 #[inline]
157 fn output_offsets(
158 &self,
159 node: NodeIndex,
160 ) -> impl Iterator<Item = PortOffset<Self::PortOffsetBase>> + Clone {
161 self.port_offsets(node, Direction::Outgoing)
162 }
163
164 /// Returns whether the port graph contains the `node`.
165 #[must_use]
166 fn contains_node(&self, node: NodeIndex) -> bool;
167
168 /// Returns whether the port graph contains the `port`.
169 #[must_use]
170 fn contains_port(&self, port: PortIndex) -> bool;
171
172 /// Returns whether the port graph has no nodes nor ports.
173 #[must_use]
174 fn is_empty(&self) -> bool;
175
176 /// Returns the number of nodes in the port graph.
177 #[must_use]
178 fn node_count(&self) -> usize;
179
180 /// Returns the number of ports in the port graph.
181 #[must_use]
182 fn port_count(&self) -> usize;
183
184 /// Iterates over the nodes in the port graph.
185 #[must_use]
186 fn nodes_iter(&self) -> impl Iterator<Item = NodeIndex> + Clone;
187
188 /// Iterates over the ports in the port graph.
189 #[must_use]
190 fn ports_iter(&self) -> impl Iterator<Item = PortIndex> + Clone;
191
192 /// Returns the capacity of the underlying buffer for nodes.
193 #[must_use]
194 fn node_capacity(&self) -> usize;
195
196 /// Returns the capacity of the underlying buffer for ports.
197 #[must_use]
198 fn port_capacity(&self) -> usize;
199
200 /// Returns the allocated port capacity for a specific node.
201 ///
202 /// Changes to the number of ports of the node will not reallocate
203 /// until the number of ports exceeds this capacity.
204 #[must_use]
205 fn node_port_capacity(&self, node: NodeIndex) -> usize;
206}
207
208/// Core capabilities for mutating a graph containing nodes and ports.
209pub trait PortMut: PortView {
210 /// Adds a node to the portgraph with a given number of input and output ports.
211 ///
212 /// # Panics
213 ///
214 /// Panics if the total number of ports exceeds `u16::MAX`.
215 ///
216 /// # Example
217 ///
218 /// ```
219 /// # use ::portgraph::*;
220 /// let mut g: PortGraph = PortGraph::new();
221 /// let node = g.add_node(4, 3);
222 /// assert_eq!(g.inputs(node).count(), 4);
223 /// assert_eq!(g.outputs(node).count(), 3);
224 /// assert!(g.contains_node(node));
225 /// ```
226 fn add_node(&mut self, incoming: usize, outgoing: usize) -> NodeIndex;
227
228 /// Remove a node from the port graph. All ports of the node will be
229 /// unlinked and removed as well. Does nothing if the node does not exist.
230 ///
231 /// # Example
232 ///
233 /// ```
234 /// # use ::portgraph::*;
235 /// let mut g: PortGraph = PortGraph::new();
236 /// let node0 = g.add_node(1, 1);
237 /// let node1 = g.add_node(1, 1);
238 /// let out0 = g.outputs(node0).nth(0).unwrap();
239 /// let out1 = g.outputs(node1).nth(0).unwrap();
240 /// let in0 = g.inputs(node0).nth(0).unwrap();
241 /// let in1 = g.inputs(node1).nth(0).unwrap();
242 /// g.link_ports(out0, in1);
243 /// g.link_ports(out1, in0);
244 /// g.remove_node(node0);
245 /// assert!(!g.contains_node(node0));
246 /// assert!(g.port_link(g.outputs(node1).nth(0).unwrap()).is_none());
247 /// assert!(g.port_link(g.inputs(node1).nth(0).unwrap()).is_none());
248 /// ```
249 fn remove_node(&mut self, node: NodeIndex);
250
251 /// Removes all nodes and ports from the port graph.
252 fn clear(&mut self);
253
254 /// Reserves enough capacity to insert at least the given number of additional nodes and ports.
255 ///
256 /// This method does not take into account the length of the free list and might overallocate speculatively.
257 fn reserve(&mut self, nodes: usize, ports: usize);
258
259 /// Changes the number of ports of the `node` to the given `incoming` and `outgoing` counts.
260 ///
261 /// Invalidates the indices of the node's ports. If the number of incoming or outgoing ports
262 /// is reduced, the ports are removed from the end of the port list.
263 ///
264 /// Every time a port is moved, the `rekey` function will be called with its old and new index.
265 /// If the port is removed, the new index will be `None`.
266 ///
267 /// This operation is O(n) where n is the number of ports of the node.
268 fn set_num_ports<F>(&mut self, node: NodeIndex, incoming: usize, outgoing: usize, rekey: F)
269 where
270 F: FnMut(PortIndex, PortOperation);
271
272 /// Swaps the indices of two nodes.
273 fn swap_nodes(&mut self, a: NodeIndex, b: NodeIndex);
274
275 /// Compacts the storage of nodes in the portgraph as much as possible. Note
276 /// that node indices won't necessarily be consecutive after this process.
277 ///
278 /// Every time a node is moved, the `rekey` function will be called with its
279 /// old and new index.
280 fn compact_nodes<F>(&mut self, rekey: F)
281 where
282 F: FnMut(NodeIndex, NodeIndex);
283
284 /// Compacts the storage of ports in the portgraph as much as possible. Note
285 /// that indices won't necessarily be consecutive after this process.
286 ///
287 /// Every time a port is moved, the `rekey` function will be called with is
288 /// old and new index.
289 fn compact_ports<F>(&mut self, rekey: F)
290 where
291 F: FnMut(PortIndex, PortIndex);
292
293 /// Shrinks the underlying buffers to the fit the data.
294 ///
295 /// This does not alter existing indices.
296 fn shrink_to_fit(&mut self);
297}
298
299/// Operations pertaining the adjacency of nodes in a port graph.
300pub trait LinkView: PortView {
301 /// The identifier for the endpoints of a link.
302 type LinkEndpoint: Into<PortIndex> + Copy;
303
304 /// Returns an iterator over every pair of matching ports connecting `from`
305 /// to `to`, i.e. in that direction.
306 ///
307 /// # Example
308 /// ```
309 /// # use ::portgraph::*;
310 /// let mut g: PortGraph = PortGraph::new();
311 /// let a = g.add_node(0, 2);
312 /// let b = g.add_node(2, 0);
313 ///
314 /// g.link_nodes(a, 0, b, 0).unwrap();
315 /// g.link_nodes(a, 1, b, 1).unwrap();
316 ///
317 /// let mut connections = g.get_connections(a, b);
318 /// assert_eq!(connections.next(), Some((g.output(a,0).unwrap(), g.input(b,0).unwrap())));
319 /// assert_eq!(connections.next(), Some((g.output(a,1).unwrap(), g.input(b,1).unwrap())));
320 /// assert_eq!(connections.next(), None);
321 /// ```
322 #[must_use]
323 fn get_connections(
324 &self,
325 from: NodeIndex,
326 to: NodeIndex,
327 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone;
328
329 /// Checks whether there is a directed link between the two nodes and
330 /// returns the first matching pair of ports.
331 ///
332 /// # Example
333 /// ```
334 /// # use ::portgraph::*;
335 /// let mut g: PortGraph = PortGraph::new();
336 /// let a = g.add_node(0, 2);
337 /// let b = g.add_node(2, 0);
338 ///
339 /// g.link_nodes(a, 0, b, 0).unwrap();
340 /// g.link_nodes(a, 1, b, 1).unwrap();
341 ///
342 /// assert_eq!(g.get_connection(a, b), Some((g.output(a,0).unwrap(), g.input(b,0).unwrap())));
343 /// ```
344 #[must_use]
345 fn get_connection(
346 &self,
347 from: NodeIndex,
348 to: NodeIndex,
349 ) -> Option<(Self::LinkEndpoint, Self::LinkEndpoint)> {
350 self.get_connections(from, to).next()
351 }
352
353 /// Checks whether there is a directed link between the two nodes.
354 ///
355 /// # Example
356 /// ```
357 /// # use ::portgraph::*;
358 /// let mut g: PortGraph = PortGraph::new();
359 /// let a = g.add_node(0, 2);
360 /// let b = g.add_node(2, 0);
361 ///
362 /// g.link_nodes(a, 0, b, 0).unwrap();
363 ///
364 /// assert!(g.connected(a, b));
365 /// ```
366 #[must_use]
367 #[inline]
368 fn connected(&self, from: NodeIndex, to: NodeIndex) -> bool {
369 self.get_connection(from, to).is_some()
370 }
371
372 /// Returns the port (or ports if this is a [`MultiView`]) that the given `port` is linked to.
373 #[must_use]
374 fn port_links(
375 &self,
376 port: PortIndex,
377 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone;
378
379 /// Return the link to the provided port, if not connected return None.
380 /// If this port has multiple connected subports, an arbitrary one is returned.
381 #[must_use]
382 #[inline]
383 fn port_link(&self, port: PortIndex) -> Option<Self::LinkEndpoint> {
384 self.port_links(port).next().map(|(_, p)| p)
385 }
386
387 /// Iterates over the connected links of the `node` in the given
388 /// `direction`.
389 ///
390 /// # Examples
391 ///
392 /// ```
393 /// # use ::portgraph::*;
394 ///
395 /// let mut graph: PortGraph = PortGraph::new();
396 ///
397 /// let node_a = graph.add_node(0, 2);
398 /// let node_b = graph.add_node(1, 0);
399 ///
400 /// let port_a = graph.outputs(node_a).next().unwrap();
401 /// let port_b = graph.inputs(node_b).next().unwrap();
402 ///
403 /// graph.link_ports(port_a, port_b).unwrap();
404 ///
405 /// assert!(graph.links(node_a, Direction::Outgoing).eq([(port_a, port_b)]));
406 /// assert!(graph.links(node_b, Direction::Incoming).eq([(port_b, port_a)]));
407 /// ```
408 #[must_use]
409 fn links(
410 &self,
411 node: NodeIndex,
412 direction: Direction,
413 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone;
414
415 /// Iterates over the connected input and output links of the `node` in sequence.
416 #[must_use]
417 fn all_links(
418 &self,
419 node: NodeIndex,
420 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone;
421
422 /// Iterates over the connected input links of the `node`. Shorthand for
423 /// [`LinkView::links`]`(`[`Direction::Incoming`]`)`.
424 #[must_use]
425 #[inline]
426 fn input_links(
427 &self,
428 node: NodeIndex,
429 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone {
430 self.links(node, Direction::Incoming)
431 }
432
433 /// Iterates over the connected output links of the `node`. Shorthand for
434 /// [`LinkView::links`]`(`[`Direction::Outgoing`]`)`..
435 #[must_use]
436 #[inline]
437 fn output_links(
438 &self,
439 node: NodeIndex,
440 ) -> impl Iterator<Item = (Self::LinkEndpoint, Self::LinkEndpoint)> + Clone {
441 self.links(node, Direction::Outgoing)
442 }
443
444 /// Iterates over neighbour nodes in the given `direction`.
445 /// May contain duplicates if the graph has multiple links between nodes.
446 ///
447 /// # Examples
448 ///
449 /// ```
450 /// # use ::portgraph::*;
451 ///
452 /// let mut graph: PortGraph = PortGraph::new();
453 ///
454 /// let a = graph.add_node(0, 1);
455 /// let b = graph.add_node(2, 1);
456 ///
457 /// graph.link_nodes(a, 0, b, 0).unwrap();
458 /// graph.link_nodes(b, 0, b, 1).unwrap();
459 ///
460 /// assert!(graph.neighbours(a, Direction::Outgoing).eq([b]));
461 /// assert!(graph.neighbours(b, Direction::Incoming).eq([a,b]));
462 /// ```
463 #[must_use]
464 fn neighbours(
465 &self,
466 node: NodeIndex,
467 direction: Direction,
468 ) -> impl Iterator<Item = NodeIndex> + Clone;
469
470 /// Iterates over the input and output neighbours of the `node` in sequence.
471 #[must_use]
472 fn all_neighbours(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> + Clone;
473
474 /// Iterates over the input neighbours of the `node`.
475 ///
476 /// Shorthand for [`LinkView::neighbours`]`(`[`Direction::Incoming`]`)`.
477 #[must_use]
478 #[inline]
479 fn input_neighbours(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> + Clone {
480 self.neighbours(node, Direction::Incoming)
481 }
482
483 /// Iterates over the output neighbours of the `node`.
484 ///
485 /// Shorthand for [`LinkView::neighbours`]`(`[`Direction::Outgoing`]`)`.
486 #[must_use]
487 #[inline]
488 fn output_neighbours(&self, node: NodeIndex) -> impl Iterator<Item = NodeIndex> + Clone {
489 self.neighbours(node, Direction::Outgoing)
490 }
491
492 /// Returns the number of links between ports.
493 #[must_use]
494 fn link_count(&self) -> usize;
495}
496
497/// Mutating operations pertaining the adjacency of nodes in a port graph.
498pub trait LinkMut: LinkView + PortMut {
499 /// Link an output port to an input port.
500 ///
501 /// # Example
502 ///
503 /// ```
504 /// # use ::portgraph::*;
505 /// let mut g: PortGraph = PortGraph::new();
506 /// let node0 = g.add_node(0, 1);
507 /// let node1 = g.add_node(1, 0);
508 /// let node0_output = g.output(node0, 0).unwrap();
509 /// let node1_input = g.input(node1, 0).unwrap();
510 /// g.link_ports(node0_output, node1_input).unwrap();
511 /// assert_eq!(g.port_link(node0_output), Some(node1_input));
512 /// assert_eq!(g.port_link(node1_input), Some(node0_output));
513 /// ```
514 ///
515 /// # Errors
516 ///
517 /// - If `port_a` or `port_b` does not exist.
518 /// - If `port_a` and `port_b` have the same direction.
519 #[allow(clippy::type_complexity)]
520 fn link_ports(
521 &mut self,
522 port_a: PortIndex,
523 port_b: PortIndex,
524 ) -> Result<(Self::LinkEndpoint, Self::LinkEndpoint), LinkError<Self::PortOffsetBase>>;
525
526 /// Links two nodes at an input and output port offsets.
527 ///
528 /// # Errors
529 ///
530 /// - If the ports and nodes do not exist.
531 #[allow(clippy::type_complexity)]
532 fn link_nodes(
533 &mut self,
534 from: NodeIndex,
535 from_output: usize,
536 to: NodeIndex,
537 to_input: usize,
538 ) -> Result<(Self::LinkEndpoint, Self::LinkEndpoint), LinkError<Self::PortOffsetBase>> {
539 self.link_offsets(
540 from,
541 PortOffset::new_outgoing(from_output),
542 to,
543 PortOffset::new_incoming(to_input),
544 )
545 }
546
547 /// Links two nodes at an input and output port offsets.
548 ///
549 /// # Errors
550 ///
551 /// - If the ports and nodes do not exist.
552 /// - If the ports have the same direction.
553 #[allow(clippy::type_complexity)]
554 fn link_offsets(
555 &mut self,
556 node_a: NodeIndex,
557 offset_a: PortOffset<Self::PortOffsetBase>,
558 node_b: NodeIndex,
559 offset_b: PortOffset<Self::PortOffsetBase>,
560 ) -> Result<(Self::LinkEndpoint, Self::LinkEndpoint), LinkError<Self::PortOffsetBase>> {
561 let from_port = self
562 .port_index(node_a, offset_a)
563 .ok_or(LinkError::UnknownOffset {
564 node: node_a,
565 offset: offset_a,
566 })?;
567 let to_port = self
568 .port_index(node_b, offset_b)
569 .ok_or(LinkError::UnknownOffset {
570 node: node_b,
571 offset: offset_b,
572 })?;
573 self.link_ports(from_port, to_port)
574 }
575
576 /// Unlinks all connections to the `port`. If the port was connected,
577 /// returns one of the ports it was connected to.
578 fn unlink_port(&mut self, port: PortIndex) -> Option<Self::LinkEndpoint>;
579
580 /// Inserts another graph into this graph.
581 ///
582 /// Returns a map from the old node indices to the new node indices.
583 fn insert_graph(
584 &mut self,
585 other: &impl LinkView<PortOffsetBase = Self::PortOffsetBase>,
586 ) -> Result<HashMap<NodeIndex, NodeIndex>, LinkError<Self::PortOffsetBase>> {
587 let nodes = other.nodes_iter();
588
589 // If we can cheaply compute the number of nodes and ports, we can
590 // reserve space for them in advance. This won't be the case e.g. for
591 // a `Region` view.
592 let num_nodes_hint = nodes.size_hint().1.unwrap_or_default();
593 let num_ports_hint = other.ports_iter().size_hint().1.unwrap_or_default();
594 self.reserve(num_nodes_hint, num_ports_hint);
595 let mut rekeys = HashMap::with_capacity(num_nodes_hint);
596
597 for old in nodes {
598 let new = self.add_node(other.num_inputs(old), other.num_outputs(old));
599 rekeys.insert(old, new);
600 for (from, to) in other.all_links(old) {
601 // If the other node has already been inserted, we can link
602 let Some(&other_node) = rekeys.get(&other.port_node(to).unwrap()) else {
603 continue;
604 };
605 self.link_offsets(
606 new,
607 other.port_offset(from).unwrap(),
608 other_node,
609 other.port_offset(to).unwrap(),
610 )?;
611 }
612 }
613 Ok(rekeys)
614 }
615}
616
617/// Abstraction over a portgraph that may have multiple connections per node.
618pub trait MultiView: LinkView {
619 /// Return the subport linked to the given `port`. If the port is not
620 /// connected, return None.
621 #[must_use]
622 fn subport_link(&self, subport: Self::LinkEndpoint) -> Option<Self::LinkEndpoint>;
623
624 /// Iterates over all the subports of the `node` in the given `direction`.
625 #[must_use]
626 fn subports(
627 &self,
628 node: NodeIndex,
629 direction: Direction,
630 ) -> impl Iterator<Item = Self::LinkEndpoint> + Clone;
631
632 /// Iterates over the input and output subports of the `node` in sequence.
633 #[must_use]
634 fn all_subports(&self, node: NodeIndex) -> impl Iterator<Item = Self::LinkEndpoint> + Clone;
635
636 /// Iterates over all the input subports of the `node`.
637 ///
638 /// Shorthand for [`MultiView::subports`].
639 #[must_use]
640 #[inline]
641 fn subport_inputs(&self, node: NodeIndex) -> impl Iterator<Item = Self::LinkEndpoint> + Clone {
642 self.subports(node, Direction::Incoming)
643 }
644
645 /// Iterates over all the output subports of the `node`.
646 ///
647 /// Shorthand for [`MultiView::subports`].
648 #[must_use]
649 #[inline]
650 fn subport_outputs(&self, node: NodeIndex) -> impl Iterator<Item = Self::LinkEndpoint> + Clone {
651 self.subports(node, Direction::Outgoing)
652 }
653}
654
655/// Abstraction for mutating a portgraph that may have multiple connections per node.
656pub trait MultiMut: MultiView + LinkMut {
657 /// Link an output subport to an input subport.
658 ///
659 /// # Errors
660 ///
661 /// - If `subport_from` or `subport_to` does not exist.
662 /// - If `subport_a` and `subport_b` have the same direction.
663 /// - If `subport_from` or `subport_to` is already linked.
664 fn link_subports(
665 &mut self,
666 subport_from: Self::LinkEndpoint,
667 subport_to: Self::LinkEndpoint,
668 ) -> Result<(), LinkError<Self::PortOffsetBase>>;
669
670 /// Unlinks the `port` and returns the subport it was linked to. Returns `None`
671 /// when the port was not linked.
672 fn unlink_subport(&mut self, subport: Self::LinkEndpoint) -> Option<Self::LinkEndpoint>;
673}