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}