flow_graph/schematic/
iterators.rs

1mod connection;
2mod node;
3mod port;
4
5pub use connection::*;
6pub use node::*;
7pub use port::*;
8
9use crate::node::NodePort;
10use crate::port::PortDirection;
11use crate::{Connection, ConnectionIndex, PortReference, Schematic};
12
13#[derive(Debug, Clone)]
14#[non_exhaustive]
15pub enum SchematicHop<'graph, DATA>
16where
17  DATA: Clone,
18{
19  Node(NodeHop<'graph, DATA>),
20  Port(Port<'graph, DATA>),
21  Ports(Ports<'graph, DATA>),
22  Connections(Connections<'graph, DATA>),
23  Connection(ConnectionRef<'graph, DATA>),
24}
25
26impl<'graph, DATA> std::fmt::Display for SchematicHop<'graph, DATA>
27where
28  DATA: Clone,
29{
30  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31    write!(
32      f,
33      "{}",
34      match self {
35        SchematicHop::Node(v) => format!("Node:{}", v.name()),
36        SchematicHop::Port(v) => format!("Port:{}", v),
37        SchematicHop::Ports(v) => format!("Ports:{}", v),
38        SchematicHop::Connections(v) => format!("Connections:{}", v),
39        SchematicHop::Connection(v) => format!("Connection:{}", v),
40      }
41    )
42  }
43}
44
45impl<'graph, DATA> From<NodeHop<'graph, DATA>> for SchematicHop<'graph, DATA>
46where
47  DATA: Clone,
48{
49  fn from(node: NodeHop<'graph, DATA>) -> Self {
50    Self::Node(node)
51  }
52}
53
54#[derive(Debug, Clone, Copy)]
55#[must_use]
56#[non_exhaustive]
57pub enum WalkDirection {
58  Up,
59  Down,
60}
61
62#[derive(Debug)]
63#[must_use]
64pub struct SchematicWalker<'graph, DATA>
65where
66  DATA: Clone,
67{
68  schematic: &'graph Schematic<DATA>,
69  last_hop: Option<SchematicHop<'graph, DATA>>,
70  hop_queue: Vec<SchematicHop<'graph, DATA>>,
71  direction: WalkDirection,
72}
73
74impl<'graph, DATA> SchematicWalker<'graph, DATA>
75where
76  DATA: Clone,
77{
78  pub fn new_from_input(schematic: &'graph Schematic<DATA>) -> Self {
79    let inputs = NodeHop::new(schematic, schematic.input).into_inputs();
80    let hop_queue = vec![SchematicHop::Ports(inputs)];
81    Self {
82      schematic,
83      last_hop: None,
84      hop_queue,
85      direction: WalkDirection::Down,
86    }
87  }
88
89  pub fn new_from_output(schematic: &'graph Schematic<DATA>) -> Self {
90    let hop_queue = vec![SchematicHop::Node(NodeHop::new(schematic, schematic.output))];
91    Self {
92      schematic,
93      last_hop: None,
94      hop_queue,
95      direction: WalkDirection::Up,
96    }
97  }
98
99  pub fn from_port(schematic: &'graph Schematic<DATA>, port: PortReference, direction: WalkDirection) -> Self {
100    let port = Port::new(schematic, port);
101    let hop_queue = vec![SchematicHop::Port(port)];
102    Self {
103      schematic,
104      last_hop: None,
105      hop_queue,
106      direction,
107    }
108  }
109}
110
111impl<'graph, DATA> Iterator for SchematicWalker<'graph, DATA>
112where
113  DATA: Clone,
114{
115  type Item = SchematicHop<'graph, DATA>;
116
117  fn next(&mut self) -> Option<SchematicHop<'graph, DATA>> {
118    let last_hop = self.last_hop.take();
119    let (mut next_hop, branch) = walk(self.schematic, last_hop, self.direction);
120
121    if let Some(branch) = branch {
122      self.hop_queue.push(branch);
123    }
124    if next_hop.is_none() {
125      next_hop = self.hop_queue.pop();
126    }
127    self.last_hop = next_hop.clone();
128    next_hop
129  }
130}
131
132#[allow(clippy::too_many_lines)]
133fn walk<'graph, DATA>(
134  schematic: &'graph Schematic<DATA>,
135  hop: Option<SchematicHop<'graph, DATA>>,
136  direction: WalkDirection,
137) -> (Option<SchematicHop<'graph, DATA>>, Option<SchematicHop<'graph, DATA>>)
138where
139  DATA: Clone,
140{
141  match hop {
142    Some(hop) => match hop {
143      SchematicHop::Node(v) => {
144        let ports = match direction {
145          WalkDirection::Up => v.into_inputs(),
146          WalkDirection::Down => v.into_outputs(),
147        };
148        if ports.is_empty() {
149          (None, None)
150        } else {
151          (Some(SchematicHop::Ports(ports)), None)
152        }
153      }
154      SchematicHop::Port(v) => match direction {
155        WalkDirection::Down => match v.port.direction {
156          PortDirection::In => {
157            let node = NodeHop::new(schematic, v.port.node_index);
158            (Some(node.into()), None)
159          }
160          PortDirection::Out => {
161            let connections = schematic
162              .get(v.port.node_index)
163              .and_then(|node| node.output_connections(v.port.port_index))
164              .map(|indices| Connections::new(schematic, indices.clone()))
165              .unwrap();
166
167            if connections.is_empty() {
168              (None, None)
169            } else {
170              (Some(SchematicHop::Connections(connections)), None)
171            }
172          }
173        },
174        WalkDirection::Up => match v.port.direction {
175          PortDirection::In => {
176            let connections = schematic
177              .get(v.port.node_index)
178              .and_then(|node| node.input_connections(v.port.port_index))
179              .map(|indices| Connections::new(schematic, indices.clone()))
180              .unwrap();
181
182            if connections.is_empty() {
183              (None, None)
184            } else {
185              (Some(SchematicHop::Connections(connections)), None)
186            }
187          }
188          PortDirection::Out => {
189            let node = NodeHop::new(schematic, v.port.node_index);
190            (Some(node.into()), None)
191          }
192        },
193      },
194      SchematicHop::Ports(mut v) => {
195        if v.direction.is_none() {
196          return (None, None);
197        }
198        let port_direction = v.direction.unwrap();
199        match direction {
200          WalkDirection::Up => match port_direction {
201            PortDirection::In => {
202              let result = v.next();
203              let rest = if result.is_none() {
204                None
205              } else {
206                Some(SchematicHop::Ports(v))
207              };
208              (result.map(SchematicHop::Port), rest)
209            }
210            PortDirection::Out => {
211              let node = NodeHop::new(schematic, v.node_index);
212              (Some(node.into()), None)
213            }
214          },
215          WalkDirection::Down => match port_direction {
216            PortDirection::In => {
217              let node = NodeHop::new(schematic, v.node_index);
218              (Some(node.into()), None)
219            }
220            PortDirection::Out => {
221              let result = v.next();
222              let rest = if result.is_none() {
223                None
224              } else {
225                Some(SchematicHop::Ports(v))
226              };
227              (result.map(SchematicHop::Port), rest)
228            }
229          },
230        }
231      }
232      SchematicHop::Connection(v) => {
233        let connection = &schematic.connections[v.index];
234        match direction {
235          WalkDirection::Up => {
236            let port = Port::new(schematic, connection.from);
237            (Some(SchematicHop::Port(port)), None)
238          }
239          WalkDirection::Down => {
240            let port = Port::new(schematic, connection.to);
241            (Some(SchematicHop::Port(port)), None)
242          }
243        }
244      }
245      SchematicHop::Connections(mut v) => {
246        let result = v.next();
247        let rest = if result.is_none() {
248          None
249        } else {
250          Some(SchematicHop::Connections(v))
251        };
252
253        (result.map(SchematicHop::Connection), rest)
254      }
255    },
256    None => (None, None),
257  }
258}
259
260fn get_ports_node<'graph, DATA>(schematic: &'graph Schematic<DATA>, port: &PortReference) -> &'graph NodePort
261where
262  DATA: Clone,
263{
264  match port.direction {
265    PortDirection::In => &schematic.nodes[port.node_index].inputs()[port.port_index],
266    PortDirection::Out => &schematic.nodes[port.node_index].outputs()[port.port_index],
267  }
268}
269
270fn get_port_name<'graph, DATA>(schematic: &'graph Schematic<DATA>, port: &PortReference) -> &'graph str
271where
272  DATA: Clone,
273{
274  match port.direction {
275    PortDirection::In => schematic.nodes[port.node_index].inputs()[port.port_index].name(),
276    PortDirection::Out => schematic.nodes[port.node_index].outputs()[port.port_index].name(),
277  }
278}
279
280fn get_port_connections<'graph, DATA>(
281  schematic: &'graph Schematic<DATA>,
282  port: &PortReference,
283) -> Connections<'graph, DATA>
284where
285  DATA: Clone,
286{
287  let direction = port.direction;
288  schematic
289    .get(port.node_index)
290    .and_then(|node| match direction {
291      PortDirection::In => node.input_connections(port.port_index),
292      PortDirection::Out => node.output_connections(port.port_index),
293    })
294    .map(|indices| Connections::new(schematic, indices.clone()))
295    .unwrap()
296}
297
298fn get_connection<DATA>(schematic: &Schematic<DATA>, index: ConnectionIndex) -> &Connection<DATA> {
299  &schematic.connections[index]
300}