Skip to main content

Graph

Struct Graph 

Source
pub struct Graph { /* private fields */ }
Expand description

The static topology of a flow-based network.

A Graph defines the structure of a network: which nodes exist, how they’re connected, and what conditions govern packet flow. The graph is immutable after creation.

§Example

use netrun_sim::graph::{Graph, Node, Edge, PortRef, PortType, Port, PortSlotSpec};
use std::collections::HashMap;

// Create a simple A -> B graph
let node_a = Node {
    name: "A".to_string(),
    in_ports: HashMap::new(),
    out_ports: [("out".to_string(), Port { slots_spec: PortSlotSpec::Infinite })].into(),
    in_salvo_conditions: HashMap::new(),
    out_salvo_conditions: HashMap::new(),
};
let node_b = Node {
    name: "B".to_string(),
    in_ports: [("in".to_string(), Port { slots_spec: PortSlotSpec::Infinite })].into(),
    out_ports: HashMap::new(),
    in_salvo_conditions: HashMap::new(),
    out_salvo_conditions: HashMap::new(),
};

let edge = Edge {
    source: PortRef { node_name: "A".to_string(), port_type: PortType::Output, port_name: "out".to_string() },
    target: PortRef { node_name: "B".to_string(), port_type: PortType::Input, port_name: "in".to_string() },
};

let graph = Graph::new(vec![node_a, node_b], vec![edge]);
assert!(graph.validate().is_empty());

Implementations§

Source§

impl Graph

Source

pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self

Creates a new Graph from a list of nodes and edges.

Builds internal indexes for efficient edge lookups by source (tail) and target (head) ports.

Examples found in repository?
examples/linear_flow.rs (line 133)
121fn create_linear_graph() -> Graph {
122    let nodes = vec![
123        create_node("A", vec![], vec!["out"]),
124        create_node("B", vec!["in"], vec!["out"]),
125        create_node("C", vec!["in"], vec![]),
126    ];
127
128    let edges = vec![
129        create_edge("A", "out", "B", "in"),
130        create_edge("B", "out", "C", "in"),
131    ];
132
133    let graph = Graph::new(nodes, edges);
134    assert!(graph.validate().is_empty(), "Graph validation failed");
135    graph
136}
More examples
Hide additional examples
examples/diamond_flow.rs (line 204)
121fn create_diamond_graph() -> Graph {
122    // Node A: source with two outputs
123    let node_a = Node {
124        name: "A".to_string(),
125        in_ports: HashMap::new(),
126        out_ports: [
127            (
128                "out1".to_string(),
129                Port {
130                    slots_spec: PortSlotSpec::Infinite,
131                },
132            ),
133            (
134                "out2".to_string(),
135                Port {
136                    slots_spec: PortSlotSpec::Infinite,
137                },
138            ),
139        ]
140        .into(),
141        in_salvo_conditions: HashMap::new(),
142        out_salvo_conditions: HashMap::new(),
143    };
144
145    // Node B: one input, one output
146    let node_b = create_simple_node("B");
147
148    // Node C: one input, one output
149    let node_c = create_simple_node("C");
150
151    // Node D: TWO inputs (requires both), no outputs
152    let node_d = Node {
153        name: "D".to_string(),
154        in_ports: [
155            (
156                "in1".to_string(),
157                Port {
158                    slots_spec: PortSlotSpec::Infinite,
159                },
160            ),
161            (
162                "in2".to_string(),
163                Port {
164                    slots_spec: PortSlotSpec::Infinite,
165                },
166            ),
167        ]
168        .into(),
169        out_ports: HashMap::new(),
170        in_salvo_conditions: [(
171            "default".to_string(),
172            SalvoCondition {
173                max_salvos: MaxSalvos::Finite(1),
174                ports: [
175                    ("in1".to_string(), PacketCount::All),
176                    ("in2".to_string(), PacketCount::All),
177                ]
178                .into_iter()
179                .collect(),
180                // Require BOTH inputs to be non-empty
181                term: SalvoConditionTerm::And(vec![
182                    SalvoConditionTerm::Port {
183                        port_name: "in1".to_string(),
184                        state: PortState::NonEmpty,
185                    },
186                    SalvoConditionTerm::Port {
187                        port_name: "in2".to_string(),
188                        state: PortState::NonEmpty,
189                    },
190                ]),
191            },
192        )]
193        .into(),
194        out_salvo_conditions: HashMap::new(),
195    };
196
197    let edges = vec![
198        create_edge("A", "out1", "B", "in"),
199        create_edge("A", "out2", "C", "in"),
200        create_edge("B", "out", "D", "in1"),
201        create_edge("C", "out", "D", "in2"),
202    ];
203
204    let graph = Graph::new(vec![node_a, node_b, node_c, node_d], edges);
205    assert!(graph.validate().is_empty(), "Graph validation failed");
206    graph
207}
Source

pub fn nodes(&self) -> &HashMap<NodeName, Node>

Returns a reference to all nodes in the graph, keyed by name.

Examples found in repository?
examples/linear_flow.rs (line 22)
19fn main() {
20    // Create a linear graph: A -> B -> C
21    let graph = create_linear_graph();
22    println!("Created graph with {} nodes", graph.nodes().len());
23
24    // Create a network from the graph
25    let mut net = NetSim::new(graph);
26
27    // Create a packet outside the network
28    let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
29        NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
30            println!("Created packet: {}", id);
31            id
32        }
33        _ => panic!("Failed to create packet"),
34    };
35
36    // Transport packet to the edge A -> B
37    let edge_a_b = PacketLocation::Edge(Edge {
38        source: PortRef {
39            node_name: "A".to_string(),
40            port_type: PortType::Output,
41            port_name: "out".to_string(),
42        },
43        target: PortRef {
44            node_name: "B".to_string(),
45            port_type: PortType::Input,
46            port_name: "in".to_string(),
47        },
48    });
49    net.do_action(&NetAction::TransportPacketToLocation(
50        packet_id.clone(),
51        edge_a_b,
52    ));
53    println!("Placed packet on edge A -> B");
54
55    // Run the network - packet moves to B's input port and triggers an epoch
56    net.run_until_blocked();
57    println!("Ran network until blocked");
58
59    // Check for startable epochs
60    let startable = net.get_startable_epochs();
61    println!("Startable epochs: {}", startable.len());
62
63    if let Some(epoch_id) = startable.first() {
64        // Start the epoch
65        match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
66            NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
67                println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
68
69                // In a real scenario, external code would process the packet here
70                // For this example, we'll just consume it and create an output
71
72                // Consume the input packet
73                net.do_action(&NetAction::ConsumePacket(packet_id));
74                println!("Consumed input packet");
75
76                // Create an output packet
77                let output_packet =
78                    match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
79                        NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
80                        _ => panic!("Failed to create output packet"),
81                    };
82                println!("Created output packet: {}", output_packet);
83
84                // Load it into the output port
85                net.do_action(&NetAction::LoadPacketIntoOutputPort(
86                    output_packet.clone(),
87                    "out".to_string(),
88                ));
89                println!("Loaded packet into output port");
90
91                // Send the output salvo
92                net.do_action(&NetAction::SendOutputSalvo(
93                    epoch.id.clone(),
94                    "default".to_string(),
95                ));
96                println!("Sent output salvo - packet is now on edge B -> C");
97
98                // Finish the epoch
99                net.do_action(&NetAction::FinishEpoch(epoch.id));
100                println!("Finished epoch");
101
102                // Run the network again - packet moves to C
103                net.run_until_blocked();
104                println!("Ran network until blocked again");
105
106                // Check for new startable epochs at C
107                let startable_c = net.get_startable_epochs();
108                println!(
109                    "New startable epochs (should be at C): {}",
110                    startable_c.len()
111                );
112            }
113            _ => panic!("Failed to start epoch"),
114        }
115    }
116
117    println!("\nLinear flow example complete!");
118}
Source

pub fn edges(&self) -> &HashSet<Edge>

Returns a reference to all edges in the graph.

Source

pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&Edge>

Returns the edge that has the given output port as its source (tail).

Source

pub fn get_edge_by_head(&self, input_port_ref: &PortRef) -> Option<&Edge>

Returns the edge that has the given input port as its target (head).

Source

pub fn validate(&self) -> Vec<GraphValidationError>

Validates the graph structure.

Returns a list of all validation errors found. An empty list means the graph is valid.

Examples found in repository?
examples/linear_flow.rs (line 134)
121fn create_linear_graph() -> Graph {
122    let nodes = vec![
123        create_node("A", vec![], vec!["out"]),
124        create_node("B", vec!["in"], vec!["out"]),
125        create_node("C", vec!["in"], vec![]),
126    ];
127
128    let edges = vec![
129        create_edge("A", "out", "B", "in"),
130        create_edge("B", "out", "C", "in"),
131    ];
132
133    let graph = Graph::new(nodes, edges);
134    assert!(graph.validate().is_empty(), "Graph validation failed");
135    graph
136}
More examples
Hide additional examples
examples/diamond_flow.rs (line 205)
121fn create_diamond_graph() -> Graph {
122    // Node A: source with two outputs
123    let node_a = Node {
124        name: "A".to_string(),
125        in_ports: HashMap::new(),
126        out_ports: [
127            (
128                "out1".to_string(),
129                Port {
130                    slots_spec: PortSlotSpec::Infinite,
131                },
132            ),
133            (
134                "out2".to_string(),
135                Port {
136                    slots_spec: PortSlotSpec::Infinite,
137                },
138            ),
139        ]
140        .into(),
141        in_salvo_conditions: HashMap::new(),
142        out_salvo_conditions: HashMap::new(),
143    };
144
145    // Node B: one input, one output
146    let node_b = create_simple_node("B");
147
148    // Node C: one input, one output
149    let node_c = create_simple_node("C");
150
151    // Node D: TWO inputs (requires both), no outputs
152    let node_d = Node {
153        name: "D".to_string(),
154        in_ports: [
155            (
156                "in1".to_string(),
157                Port {
158                    slots_spec: PortSlotSpec::Infinite,
159                },
160            ),
161            (
162                "in2".to_string(),
163                Port {
164                    slots_spec: PortSlotSpec::Infinite,
165                },
166            ),
167        ]
168        .into(),
169        out_ports: HashMap::new(),
170        in_salvo_conditions: [(
171            "default".to_string(),
172            SalvoCondition {
173                max_salvos: MaxSalvos::Finite(1),
174                ports: [
175                    ("in1".to_string(), PacketCount::All),
176                    ("in2".to_string(), PacketCount::All),
177                ]
178                .into_iter()
179                .collect(),
180                // Require BOTH inputs to be non-empty
181                term: SalvoConditionTerm::And(vec![
182                    SalvoConditionTerm::Port {
183                        port_name: "in1".to_string(),
184                        state: PortState::NonEmpty,
185                    },
186                    SalvoConditionTerm::Port {
187                        port_name: "in2".to_string(),
188                        state: PortState::NonEmpty,
189                    },
190                ]),
191            },
192        )]
193        .into(),
194        out_salvo_conditions: HashMap::new(),
195    };
196
197    let edges = vec![
198        create_edge("A", "out1", "B", "in"),
199        create_edge("A", "out2", "C", "in"),
200        create_edge("B", "out", "D", "in1"),
201        create_edge("C", "out", "D", "in2"),
202    ];
203
204    let graph = Graph::new(vec![node_a, node_b, node_c, node_d], edges);
205    assert!(graph.validate().is_empty(), "Graph validation failed");
206    graph
207}

Trait Implementations§

Source§

impl Clone for Graph

Source§

fn clone(&self) -> Graph

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Graph

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Graph

§

impl RefUnwindSafe for Graph

§

impl Send for Graph

§

impl Sync for Graph

§

impl Unpin for Graph

§

impl UnwindSafe for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V