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}