netrun-sim 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/Cargo.lock +0 -64
  2. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/PKG-INFO +1 -1
  3. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/Cargo.toml +3 -4
  4. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/examples/diamond_flow.rs +14 -17
  5. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/examples/linear_flow.rs +14 -17
  6. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/graph.rs +71 -116
  7. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/net.rs +14 -14
  8. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/test_fixtures.rs +4 -5
  9. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/common/mod.rs +4 -5
  10. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/graph_api.rs +1 -54
  11. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/net_api.rs +2 -2
  12. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/workflow.rs +13 -13
  13. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/pyproject.toml +1 -1
  14. netrun_sim-0.1.1/python/CHANGELOG.md +19 -0
  15. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/Cargo.toml +0 -1
  16. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/README.md +6 -10
  17. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/diamond_flow.ipynb +29 -32
  18. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/diamond_flow.py +5 -9
  19. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/linear_flow.ipynb +10 -13
  20. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/linear_flow.py +5 -9
  21. {netrun_sim-0.1.0/python → netrun_sim-0.1.1}/python/netrun_sim/__init__.pyi +4 -15
  22. {netrun_sim-0.1.0 → netrun_sim-0.1.1/python}/python/netrun_sim/__init__.pyi +4 -15
  23. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/errors.rs +2 -2
  24. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/graph.rs +24 -62
  25. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/net.rs +3 -3
  26. netrun_sim-0.1.0/python/CHANGELOG.md +0 -6
  27. netrun_sim-0.1.0/python/examples/graph_serialization.ipynb +0 -542
  28. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/Cargo.toml +0 -0
  29. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/_utils.rs +0 -0
  30. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/lib.rs +0 -0
  31. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/.envrc +0 -0
  32. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/netrun_sim/__init__.py +0 -0
  33. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/netrun_sim/py.typed +0 -0
  34. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/publish_new_version.sh +0 -0
  35. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/python/netrun_sim/__init__.py +0 -0
  36. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/python/netrun_sim/py.typed +0 -0
  37. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/lib.rs +0 -0
  38. {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/uv.lock +0 -0
@@ -90,12 +90,6 @@ dependencies = [
90
90
  "rustversion",
91
91
  ]
92
92
 
93
- [[package]]
94
- name = "itoa"
95
- version = "1.0.16"
96
- source = "registry+https://github.com/rust-lang/crates.io-index"
97
- checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
98
-
99
93
  [[package]]
100
94
  name = "js-sys"
101
95
  version = "0.3.83"
@@ -118,12 +112,6 @@ version = "0.4.15"
118
112
  source = "registry+https://github.com/rust-lang/crates.io-index"
119
113
  checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
120
114
 
121
- [[package]]
122
- name = "memchr"
123
- version = "2.7.6"
124
- source = "registry+https://github.com/rust-lang/crates.io-index"
125
- checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
126
-
127
115
  [[package]]
128
116
  name = "memoffset"
129
117
  version = "0.9.1"
@@ -138,8 +126,6 @@ name = "netrun-sim"
138
126
  version = "0.1.0"
139
127
  dependencies = [
140
128
  "indexmap",
141
- "serde",
142
- "serde_json",
143
129
  "thiserror",
144
130
  "ulid",
145
131
  "utcnow",
@@ -151,7 +137,6 @@ version = "0.1.0"
151
137
  dependencies = [
152
138
  "netrun-sim",
153
139
  "pyo3",
154
- "serde_json",
155
140
  "ulid",
156
141
  ]
157
142
 
@@ -306,49 +291,6 @@ version = "1.0.22"
306
291
  source = "registry+https://github.com/rust-lang/crates.io-index"
307
292
  checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
308
293
 
309
- [[package]]
310
- name = "serde"
311
- version = "1.0.228"
312
- source = "registry+https://github.com/rust-lang/crates.io-index"
313
- checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
314
- dependencies = [
315
- "serde_core",
316
- "serde_derive",
317
- ]
318
-
319
- [[package]]
320
- name = "serde_core"
321
- version = "1.0.228"
322
- source = "registry+https://github.com/rust-lang/crates.io-index"
323
- checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
324
- dependencies = [
325
- "serde_derive",
326
- ]
327
-
328
- [[package]]
329
- name = "serde_derive"
330
- version = "1.0.228"
331
- source = "registry+https://github.com/rust-lang/crates.io-index"
332
- checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
333
- dependencies = [
334
- "proc-macro2",
335
- "quote",
336
- "syn",
337
- ]
338
-
339
- [[package]]
340
- name = "serde_json"
341
- version = "1.0.147"
342
- source = "registry+https://github.com/rust-lang/crates.io-index"
343
- checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4"
344
- dependencies = [
345
- "itoa",
346
- "memchr",
347
- "serde",
348
- "serde_core",
349
- "zmij",
350
- ]
351
-
352
294
  [[package]]
353
295
  name = "syn"
354
296
  version = "2.0.111"
@@ -604,9 +546,3 @@ dependencies = [
604
546
  "quote",
605
547
  "syn",
606
548
  ]
607
-
608
- [[package]]
609
- name = "zmij"
610
- version = "0.1.8"
611
- source = "registry+https://github.com/rust-lang/crates.io-index"
612
- checksum = "f1dccf46b25b205e4bebe1d5258a991df1cc17801017a845cb5b3fe0269781aa"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netrun-sim
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Requires-Dist: python-ulid>=1.0
@@ -1,14 +1,13 @@
1
1
  [package]
2
2
  name = "netrun-sim"
3
+ description = "A flow-based development (FBD) simulation engine."
3
4
  version = "0.1.0"
4
5
  edition = "2024"
6
+ license-file = "../../LICENSE"
7
+ repository = "https://github.com/lukastk/netrun/"
5
8
 
6
9
  [dependencies]
7
10
  indexmap = "2.12.1"
8
- serde = { version = "1.0", features = ["derive"] }
9
11
  thiserror = "2.0"
10
12
  ulid = "=1.1.0"
11
13
  utcnow = "0.2.7"
12
-
13
- [dev-dependencies]
14
- serde_json = "1.0"
@@ -13,7 +13,7 @@
13
13
  //! - Synchronization: D's epoch only triggers when both inputs are present
14
14
 
15
15
  use netrun_sim::graph::{
16
- Edge, EdgeRef, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
16
+ Edge, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
17
17
  SalvoCondition, SalvoConditionTerm,
18
18
  };
19
19
  use netrun_sim::net::{
@@ -190,26 +190,23 @@ fn create_simple_node(name: &str) -> Node {
190
190
  }
191
191
  }
192
192
 
193
- fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> (EdgeRef, Edge) {
194
- (
195
- EdgeRef {
196
- source: PortRef {
197
- node_name: src_node.to_string(),
198
- port_type: PortType::Output,
199
- port_name: src_port.to_string(),
200
- },
201
- target: PortRef {
202
- node_name: tgt_node.to_string(),
203
- port_type: PortType::Input,
204
- port_name: tgt_port.to_string(),
205
- },
193
+ fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
194
+ Edge {
195
+ source: PortRef {
196
+ node_name: src_node.to_string(),
197
+ port_type: PortType::Output,
198
+ port_name: src_port.to_string(),
206
199
  },
207
- Edge {},
208
- )
200
+ target: PortRef {
201
+ node_name: tgt_node.to_string(),
202
+ port_type: PortType::Input,
203
+ port_name: tgt_port.to_string(),
204
+ },
205
+ }
209
206
  }
210
207
 
211
208
  fn edge_location(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> PacketLocation {
212
- PacketLocation::Edge(EdgeRef {
209
+ PacketLocation::Edge(Edge {
213
210
  source: PortRef {
214
211
  node_name: src_node.to_string(),
215
212
  port_type: PortType::Output,
@@ -8,7 +8,7 @@
8
8
  //! - Sending output salvos to continue flow
9
9
 
10
10
  use netrun_sim::graph::{
11
- Edge, EdgeRef, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
11
+ Edge, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
12
12
  SalvoCondition, SalvoConditionTerm,
13
13
  };
14
14
  use netrun_sim::net::{
@@ -34,7 +34,7 @@ fn main() {
34
34
  };
35
35
 
36
36
  // Transport packet to the edge A -> B
37
- let edge_a_b = PacketLocation::Edge(EdgeRef {
37
+ let edge_a_b = PacketLocation::Edge(Edge {
38
38
  source: PortRef {
39
39
  node_name: "A".to_string(),
40
40
  port_type: PortType::Output,
@@ -174,20 +174,17 @@ fn create_node(name: &str, in_ports: Vec<&str>, out_ports: Vec<&str>) -> Node {
174
174
  }
175
175
  }
176
176
 
177
- fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> (EdgeRef, Edge) {
178
- (
179
- EdgeRef {
180
- source: PortRef {
181
- node_name: src_node.to_string(),
182
- port_type: PortType::Output,
183
- port_name: src_port.to_string(),
184
- },
185
- target: PortRef {
186
- node_name: tgt_node.to_string(),
187
- port_type: PortType::Input,
188
- port_name: tgt_port.to_string(),
189
- },
177
+ fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
178
+ Edge {
179
+ source: PortRef {
180
+ node_name: src_node.to_string(),
181
+ port_type: PortType::Output,
182
+ port_name: src_port.to_string(),
190
183
  },
191
- Edge {},
192
- )
184
+ target: PortRef {
185
+ node_name: tgt_node.to_string(),
186
+ port_type: PortType::Input,
187
+ port_name: tgt_port.to_string(),
188
+ },
189
+ }
193
190
  }
@@ -3,14 +3,13 @@
3
3
  //! This module defines the static structure of a network: nodes, ports, edges,
4
4
  //! and the conditions that govern packet flow (salvo conditions).
5
5
 
6
- use serde::{Deserialize, Serialize};
7
6
  use std::collections::{HashMap, HashSet};
8
7
 
9
8
  /// The name of a port on a node.
10
9
  pub type PortName = String;
11
10
 
12
11
  /// Specifies the capacity of a port (how many packets it can hold).
13
- #[derive(Debug, Clone, Serialize, Deserialize)]
12
+ #[derive(Debug, Clone)]
14
13
  pub enum PortSlotSpec {
15
14
  /// Port can hold unlimited packets.
16
15
  Infinite,
@@ -19,7 +18,7 @@ pub enum PortSlotSpec {
19
18
  }
20
19
 
21
20
  /// A port on a node where packets can enter or exit.
22
- #[derive(Debug, Clone, Serialize, Deserialize)]
21
+ #[derive(Debug, Clone)]
23
22
  pub struct Port {
24
23
  /// The capacity specification for this port.
25
24
  pub slots_spec: PortSlotSpec,
@@ -29,7 +28,7 @@ pub struct Port {
29
28
  ///
30
29
  /// These predicates test the current packet count at a port against
31
30
  /// various conditions like empty, full, or numeric comparisons.
32
- #[derive(Debug, Clone, Serialize, Deserialize)]
31
+ #[derive(Debug, Clone)]
33
32
  pub enum PortState {
34
33
  /// Port has zero packets.
35
34
  Empty,
@@ -55,7 +54,7 @@ pub enum PortState {
55
54
  ///
56
55
  /// This forms a simple expression tree that can combine port state checks
57
56
  /// with logical operators (And, Or, Not).
58
- #[derive(Debug, Clone, Serialize, Deserialize)]
57
+ #[derive(Debug, Clone)]
59
58
  pub enum SalvoConditionTerm {
60
59
  /// Check if a specific port matches a state predicate.
61
60
  Port { port_name: String, state: PortState },
@@ -130,7 +129,7 @@ pub type SalvoConditionName = String;
130
129
  /// Salvo conditions are attached to nodes and control the flow of packets:
131
130
  /// - **Input salvo conditions**: Define when packets at input ports can trigger a new epoch
132
131
  /// - **Output salvo conditions**: Define when packets at output ports can be sent out
133
- #[derive(Debug, Clone, Serialize, Deserialize)]
132
+ #[derive(Debug, Clone)]
134
133
  pub struct SalvoCondition {
135
134
  /// Maximum number of times this condition can trigger per epoch.
136
135
  /// For input salvo conditions, this must be 1.
@@ -160,7 +159,7 @@ fn collect_ports_from_term(term: &SalvoConditionTerm, ports: &mut HashSet<PortNa
160
159
  }
161
160
 
162
161
  /// Errors that can occur during graph validation
163
- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, thiserror::Error)]
162
+ #[derive(Debug, Clone, PartialEq, thiserror::Error)]
164
163
  pub enum GraphValidationError {
165
164
  /// Edge references a node that doesn't exist
166
165
  #[error("edge {edge_source} -> {edge_target} references non-existent node '{missing_node}'")]
@@ -230,7 +229,7 @@ pub type NodeName = String;
230
229
  /// - Output ports where packets are sent
231
230
  /// - Input salvo conditions that define when arriving packets trigger an epoch
232
231
  /// - Output salvo conditions that define when output packets can be sent
233
- #[derive(Debug, Clone, Serialize, Deserialize)]
232
+ #[derive(Debug, Clone)]
234
233
  pub struct Node {
235
234
  /// The unique name of this node.
236
235
  pub name: NodeName,
@@ -245,7 +244,7 @@ pub struct Node {
245
244
  }
246
245
 
247
246
  /// Whether a port is for input or output.
248
- #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
247
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
249
248
  pub enum PortType {
250
249
  /// An input port (packets flow into the node).
251
250
  Input,
@@ -254,7 +253,7 @@ pub enum PortType {
254
253
  }
255
254
 
256
255
  /// A reference to a specific port on a specific node.
257
- #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
256
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
258
257
  pub struct PortRef {
259
258
  /// The name of the node containing this port.
260
259
  pub node_name: NodeName,
@@ -277,22 +276,16 @@ impl std::fmt::Display for PortRef {
277
276
  /// A connection between two ports in the graph.
278
277
  ///
279
278
  /// Edges connect output ports to input ports, allowing packets to flow
280
- /// between nodes. Currently edges have no additional properties beyond
281
- /// their endpoints.
282
- #[derive(Debug, Clone, Serialize, Deserialize)]
279
+ /// between nodes.
280
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
283
281
  pub struct Edge {
284
- }
285
-
286
- /// A reference to an edge, identified by its source and target ports.
287
- #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
288
- pub struct EdgeRef {
289
282
  /// The output port where this edge originates.
290
283
  pub source: PortRef,
291
284
  /// The input port where this edge terminates.
292
285
  pub target: PortRef,
293
286
  }
294
287
 
295
- impl std::fmt::Display for EdgeRef {
288
+ impl std::fmt::Display for Edge {
296
289
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297
290
  write!(f, "{} -> {}", self.source, self.target)
298
291
  }
@@ -307,7 +300,7 @@ impl std::fmt::Display for EdgeRef {
307
300
  /// # Example
308
301
  ///
309
302
  /// ```
310
- /// use netrun_sim::graph::{Graph, Node, Edge, EdgeRef, PortRef, PortType, Port, PortSlotSpec};
303
+ /// use netrun_sim::graph::{Graph, Node, Edge, PortRef, PortType, Port, PortSlotSpec};
311
304
  /// use std::collections::HashMap;
312
305
  ///
313
306
  /// // Create a simple A -> B graph
@@ -326,74 +319,45 @@ impl std::fmt::Display for EdgeRef {
326
319
  /// out_salvo_conditions: HashMap::new(),
327
320
  /// };
328
321
  ///
329
- /// let edge = (
330
- /// EdgeRef {
331
- /// source: PortRef { node_name: "A".to_string(), port_type: PortType::Output, port_name: "out".to_string() },
332
- /// target: PortRef { node_name: "B".to_string(), port_type: PortType::Input, port_name: "in".to_string() },
333
- /// },
334
- /// Edge {},
335
- /// );
322
+ /// let edge = Edge {
323
+ /// source: PortRef { node_name: "A".to_string(), port_type: PortType::Output, port_name: "out".to_string() },
324
+ /// target: PortRef { node_name: "B".to_string(), port_type: PortType::Input, port_name: "in".to_string() },
325
+ /// };
336
326
  ///
337
327
  /// let graph = Graph::new(vec![node_a, node_b], vec![edge]);
338
328
  /// assert!(graph.validate().is_empty());
339
329
  /// ```
340
- #[derive(Debug, Clone, Serialize, Deserialize)]
341
- #[serde(from = "GraphData", into = "GraphData")]
330
+ #[derive(Debug, Clone)]
342
331
  pub struct Graph {
343
332
  nodes: HashMap<NodeName, Node>,
344
- edges: HashMap<EdgeRef, Edge>,
345
-
346
- #[serde(skip)]
347
- edges_by_tail: HashMap<PortRef, EdgeRef>,
348
- #[serde(skip)]
349
- edges_by_head: HashMap<PortRef, EdgeRef>,
350
- }
351
-
352
- /// Helper struct for serializing/deserializing Graph without the indexes
353
- #[derive(Serialize, Deserialize)]
354
- struct GraphData {
355
- nodes: Vec<Node>,
356
- edges: Vec<(EdgeRef, Edge)>,
357
- }
358
-
359
- impl From<GraphData> for Graph {
360
- fn from(data: GraphData) -> Self {
361
- Graph::new(data.nodes, data.edges)
362
- }
363
- }
364
-
365
- impl From<Graph> for GraphData {
366
- fn from(graph: Graph) -> Self {
367
- GraphData {
368
- nodes: graph.nodes.into_values().collect(),
369
- edges: graph.edges.into_iter().collect(),
370
- }
371
- }
333
+ edges: HashSet<Edge>,
334
+ edges_by_tail: HashMap<PortRef, Edge>,
335
+ edges_by_head: HashMap<PortRef, Edge>,
372
336
  }
373
337
 
374
338
  impl Graph {
375
339
  /// Creates a new Graph from a list of nodes and edges.
376
340
  ///
377
341
  /// Builds internal indexes for efficient edge lookups by source (tail) and target (head) ports.
378
- pub fn new(nodes: Vec<Node>, edges: Vec<(EdgeRef, Edge)>) -> Self {
342
+ pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self {
379
343
  let nodes_map: HashMap<NodeName, Node> = nodes
380
344
  .into_iter()
381
345
  .map(|node| (node.name.clone(), node))
382
346
  .collect();
383
347
 
384
- let mut edges_map: HashMap<EdgeRef, Edge> = HashMap::new();
385
- let mut edges_by_tail: HashMap<PortRef, EdgeRef> = HashMap::new();
386
- let mut edges_by_head: HashMap<PortRef, EdgeRef> = HashMap::new();
348
+ let mut edges_set: HashSet<Edge> = HashSet::new();
349
+ let mut edges_by_tail: HashMap<PortRef, Edge> = HashMap::new();
350
+ let mut edges_by_head: HashMap<PortRef, Edge> = HashMap::new();
387
351
 
388
- for (edge_ref, edge) in edges {
389
- edges_by_tail.insert(edge_ref.source.clone(), edge_ref.clone());
390
- edges_by_head.insert(edge_ref.target.clone(), edge_ref.clone());
391
- edges_map.insert(edge_ref, edge);
352
+ for edge in edges {
353
+ edges_by_tail.insert(edge.source.clone(), edge.clone());
354
+ edges_by_head.insert(edge.target.clone(), edge.clone());
355
+ edges_set.insert(edge);
392
356
  }
393
357
 
394
358
  Graph {
395
359
  nodes: nodes_map,
396
- edges: edges_map,
360
+ edges: edges_set,
397
361
  edges_by_tail,
398
362
  edges_by_head,
399
363
  }
@@ -402,16 +366,16 @@ impl Graph {
402
366
  /// Returns a reference to all nodes in the graph, keyed by name.
403
367
  pub fn nodes(&self) -> &HashMap<NodeName, Node> { &self.nodes }
404
368
 
405
- /// Returns a reference to all edges in the graph, keyed by their endpoints.
406
- pub fn edges(&self) -> &HashMap<EdgeRef, Edge> { &self.edges }
369
+ /// Returns a reference to all edges in the graph.
370
+ pub fn edges(&self) -> &HashSet<Edge> { &self.edges }
407
371
 
408
372
  /// Returns the edge that has the given output port as its source (tail).
409
- pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&EdgeRef> {
373
+ pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&Edge> {
410
374
  self.edges_by_tail.get(output_port_ref)
411
375
  }
412
376
 
413
377
  /// Returns the edge that has the given input port as its target (head).
414
- pub fn get_edge_by_head(&self, input_port_ref: &PortRef) -> Option<&EdgeRef> {
378
+ pub fn get_edge_by_head(&self, input_port_ref: &PortRef) -> Option<&Edge> {
415
379
  self.edges_by_head.get(input_port_ref)
416
380
  }
417
381
 
@@ -425,9 +389,9 @@ impl Graph {
425
389
  let mut seen_edges: HashSet<(&PortRef, &PortRef)> = HashSet::new();
426
390
 
427
391
  // Validate edges
428
- for edge_ref in self.edges.keys() {
429
- let source = &edge_ref.source;
430
- let target = &edge_ref.target;
392
+ for edge in &self.edges {
393
+ let source = &edge.source;
394
+ let target = &edge.target;
431
395
 
432
396
  // Check for duplicate edges
433
397
  if !seen_edges.insert((source, target)) {
@@ -611,22 +575,19 @@ mod tests {
611
575
  }
612
576
  }
613
577
 
614
- fn edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> (EdgeRef, Edge) {
615
- (
616
- EdgeRef {
617
- source: PortRef {
618
- node_name: src_node.to_string(),
619
- port_type: PortType::Output,
620
- port_name: src_port.to_string(),
621
- },
622
- target: PortRef {
623
- node_name: tgt_node.to_string(),
624
- port_type: PortType::Input,
625
- port_name: tgt_port.to_string(),
626
- },
578
+ fn edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
579
+ Edge {
580
+ source: PortRef {
581
+ node_name: src_node.to_string(),
582
+ port_type: PortType::Output,
583
+ port_name: src_port.to_string(),
584
+ },
585
+ target: PortRef {
586
+ node_name: tgt_node.to_string(),
587
+ port_type: PortType::Input,
588
+ port_name: tgt_port.to_string(),
627
589
  },
628
- Edge {},
629
- )
590
+ }
630
591
  }
631
592
 
632
593
  #[test]
@@ -727,21 +688,18 @@ mod tests {
727
688
  simple_node("B", vec!["in"], vec![]),
728
689
  ];
729
690
  // Edge from input port (wrong type)
730
- let edges = vec![(
731
- EdgeRef {
732
- source: PortRef {
733
- node_name: "A".to_string(),
734
- port_type: PortType::Input, // Wrong!
735
- port_name: "in".to_string(),
736
- },
737
- target: PortRef {
738
- node_name: "B".to_string(),
739
- port_type: PortType::Input,
740
- port_name: "in".to_string(),
741
- },
691
+ let edges = vec![Edge {
692
+ source: PortRef {
693
+ node_name: "A".to_string(),
694
+ port_type: PortType::Input, // Wrong!
695
+ port_name: "in".to_string(),
742
696
  },
743
- Edge {},
744
- )];
697
+ target: PortRef {
698
+ node_name: "B".to_string(),
699
+ port_type: PortType::Input,
700
+ port_name: "in".to_string(),
701
+ },
702
+ }];
745
703
  let graph = Graph::new(nodes, edges);
746
704
 
747
705
  let errors = graph.validate();
@@ -755,21 +713,18 @@ mod tests {
755
713
  simple_node("B", vec!["in"], vec!["out"]),
756
714
  ];
757
715
  // Edge to output port (wrong type)
758
- let edges = vec![(
759
- EdgeRef {
760
- source: PortRef {
761
- node_name: "A".to_string(),
762
- port_type: PortType::Output,
763
- port_name: "out".to_string(),
764
- },
765
- target: PortRef {
766
- node_name: "B".to_string(),
767
- port_type: PortType::Output, // Wrong!
768
- port_name: "out".to_string(),
769
- },
716
+ let edges = vec![Edge {
717
+ source: PortRef {
718
+ node_name: "A".to_string(),
719
+ port_type: PortType::Output,
720
+ port_name: "out".to_string(),
721
+ },
722
+ target: PortRef {
723
+ node_name: "B".to_string(),
724
+ port_type: PortType::Output, // Wrong!
725
+ port_name: "out".to_string(),
770
726
  },
771
- Edge {},
772
- )];
727
+ }];
773
728
  let graph = Graph::new(nodes, edges);
774
729
 
775
730
  let errors = graph.validate();
@@ -7,7 +7,7 @@
7
7
  //! [`NetAction`] and returns a [`NetActionResponse`] containing any events that occurred.
8
8
 
9
9
  use crate::_utils::get_utc_now;
10
- use crate::graph::{EdgeRef, Graph, NodeName, Port, PortSlotSpec, PortName, PortType, PortRef, SalvoConditionName, SalvoConditionTerm, evaluate_salvo_condition};
10
+ use crate::graph::{Edge, Graph, NodeName, Port, PortSlotSpec, PortName, PortType, PortRef, SalvoConditionName, SalvoConditionTerm, evaluate_salvo_condition};
11
11
  use indexmap::IndexSet;
12
12
  use std::collections::{HashMap, HashSet};
13
13
  use ulid::Ulid;
@@ -34,7 +34,7 @@ pub enum PacketLocation {
34
34
  /// Loaded into an epoch's output port, ready to be sent.
35
35
  OutputPort(EpochID, PortName),
36
36
  /// In transit on an edge between nodes.
37
- Edge(EdgeRef),
37
+ Edge(Edge),
38
38
  /// External to the network (not yet injected or already extracted).
39
39
  OutsideNet,
40
40
  }
@@ -211,8 +211,8 @@ pub enum NetActionError {
211
211
  CannotMovePacketIntoRunningEpoch { packet_id: PacketID, epoch_id: EpochID },
212
212
 
213
213
  /// Edge does not exist in the graph
214
- #[error("edge not found: {edge_ref}")]
215
- EdgeNotFound { edge_ref: EdgeRef },
214
+ #[error("edge not found: {edge}")]
215
+ EdgeNotFound { edge: Edge },
216
216
  }
217
217
 
218
218
  /// An event that occurred during a network action.
@@ -292,8 +292,8 @@ impl Net {
292
292
  let mut packets_by_location: HashMap<PacketLocation, IndexSet<PacketID>> = HashMap::new();
293
293
 
294
294
  // Initialize empty packet sets for all edges
295
- for edge_ref in graph.edges().keys() {
296
- packets_by_location.insert(PacketLocation::Edge(edge_ref.clone()), IndexSet::new());
295
+ for edge in graph.edges() {
296
+ packets_by_location.insert(PacketLocation::Edge(edge.clone()), IndexSet::new());
297
297
  }
298
298
 
299
299
  // Initialize empty packet sets for all input ports
@@ -1051,11 +1051,11 @@ impl Net {
1051
1051
  });
1052
1052
  }
1053
1053
  }
1054
- PacketLocation::Edge(edge_ref) => {
1054
+ PacketLocation::Edge(edge) => {
1055
1055
  // Check edge exists in graph
1056
- if !self.graph.edges().contains_key(edge_ref) {
1056
+ if !self.graph.edges().contains(edge) {
1057
1057
  return NetActionResponse::Error(NetActionError::EdgeNotFound {
1058
- edge_ref: edge_ref.clone(),
1058
+ edge: edge.clone(),
1059
1059
  });
1060
1060
  }
1061
1061
  }
@@ -1224,7 +1224,7 @@ mod tests {
1224
1224
  let packet_id = get_packet_id(&response);
1225
1225
 
1226
1226
  // Manually place packet on edge (simulating it came from node A)
1227
- let edge_location = PacketLocation::Edge(EdgeRef {
1227
+ let edge_location = PacketLocation::Edge(Edge {
1228
1228
  source: PortRef {
1229
1229
  node_name: "A".to_string(),
1230
1230
  port_type: PortType::Output,
@@ -1341,7 +1341,7 @@ mod tests {
1341
1341
 
1342
1342
  // Create packet on edge A->B
1343
1343
  let packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
1344
- let edge_location = PacketLocation::Edge(EdgeRef {
1344
+ let edge_location = PacketLocation::Edge(Edge {
1345
1345
  source: PortRef {
1346
1346
  node_name: "A".to_string(),
1347
1347
  port_type: PortType::Output,
@@ -1391,7 +1391,7 @@ mod tests {
1391
1391
  let mut net = Net::new(graph);
1392
1392
 
1393
1393
  // Create two packets on edge A->B
1394
- let edge_location = PacketLocation::Edge(EdgeRef {
1394
+ let edge_location = PacketLocation::Edge(Edge {
1395
1395
  source: PortRef {
1396
1396
  node_name: "A".to_string(),
1397
1397
  port_type: PortType::Output,
@@ -1431,7 +1431,7 @@ mod tests {
1431
1431
  let graph = linear_graph_3();
1432
1432
  let mut net = Net::new(graph);
1433
1433
 
1434
- let edge_location = PacketLocation::Edge(EdgeRef {
1434
+ let edge_location = PacketLocation::Edge(Edge {
1435
1435
  source: PortRef {
1436
1436
  node_name: "A".to_string(),
1437
1437
  port_type: PortType::Output,
@@ -1541,7 +1541,7 @@ mod tests {
1541
1541
  assert!(matches!(response, NetActionResponse::Success(NetActionResponseData::None, _)));
1542
1542
 
1543
1543
  // Packet should now be on edge B->C
1544
- let edge_loc = PacketLocation::Edge(EdgeRef {
1544
+ let edge_loc = PacketLocation::Edge(Edge {
1545
1545
  source: PortRef {
1546
1546
  node_name: "B".to_string(),
1547
1547
  port_type: PortType::Output,