use crate::graph::{
DependencyRequestConfig, DependencyRequestTrigger, Edge, Graph, MaxSalvos, Node, PacketCount,
Port, PortName, PortRef, PortSlotSpec, PortState, PortType, SalvoCondition, SalvoConditionTerm,
};
use indexmap::IndexMap;
use std::collections::HashMap;
pub fn infinite_port() -> Port {
Port {
slots_spec: PortSlotSpec::Infinite,
}
}
pub fn finite_port(capacity: u64) -> Port {
Port {
slots_spec: PortSlotSpec::Finite(capacity),
}
}
pub fn all_ports_non_empty_condition(port_names: Vec<&str>) -> SalvoCondition {
let terms: Vec<SalvoConditionTerm> = port_names
.iter()
.map(|name| SalvoConditionTerm::Port {
port_name: name.to_string(),
state: PortState::NonEmpty,
})
.collect();
SalvoCondition {
max_salvos: MaxSalvos::Finite(1),
ports: port_names
.iter()
.map(|s| (s.to_string(), PacketCount::All))
.collect(),
term: if terms.len() == 1 {
terms.into_iter().next().unwrap()
} else {
SalvoConditionTerm::And(terms)
},
}
}
pub fn output_salvo_condition(port_names: Vec<&str>, max_salvos: MaxSalvos) -> SalvoCondition {
let terms: Vec<SalvoConditionTerm> = port_names
.iter()
.map(|name| SalvoConditionTerm::Port {
port_name: name.to_string(),
state: PortState::NonEmpty,
})
.collect();
SalvoCondition {
max_salvos,
ports: port_names
.iter()
.map(|s| (s.to_string(), PacketCount::All))
.collect(),
term: if terms.len() == 1 {
terms.into_iter().next().unwrap()
} else {
SalvoConditionTerm::And(terms)
},
}
}
pub fn simple_node(name: &str, in_ports: Vec<&str>, out_ports: Vec<&str>) -> Node {
let in_ports_map: HashMap<PortName, Port> = in_ports
.iter()
.map(|p| (p.to_string(), infinite_port()))
.collect();
let out_ports_map: HashMap<PortName, Port> = out_ports
.iter()
.map(|p| (p.to_string(), infinite_port()))
.collect();
let mut in_salvo_conditions = IndexMap::new();
if !in_ports.is_empty() {
in_salvo_conditions.insert(
"default".to_string(),
all_ports_non_empty_condition(in_ports.clone()),
);
}
let mut out_salvo_conditions = IndexMap::new();
if !out_ports.is_empty() {
out_salvo_conditions.insert(
"default".to_string(),
output_salvo_condition(out_ports.clone(), MaxSalvos::Infinite),
);
}
Node {
name: name.to_string(),
in_ports: in_ports_map,
out_ports: out_ports_map,
in_salvo_conditions,
out_salvo_conditions,
dependency_request_config: None,
}
}
pub fn dependency_node(
name: &str,
in_ports: Vec<&str>,
out_ports: Vec<&str>,
triggers: Vec<DependencyRequestTrigger>,
label: &str,
) -> Node {
let mut node = simple_node(name, in_ports, out_ports);
node.dependency_request_config = Some(DependencyRequestConfig {
triggers,
label: label.to_string(),
});
node
}
pub fn edge(source_node: &str, source_port: &str, target_node: &str, target_port: &str) -> Edge {
Edge {
source: PortRef {
node_name: source_node.to_string(),
port_type: PortType::Output,
port_name: source_port.to_string(),
},
target: PortRef {
node_name: target_node.to_string(),
port_type: PortType::Input,
port_name: target_port.to_string(),
},
}
}
pub fn linear_graph_3() -> Graph {
let nodes = vec![
simple_node("A", vec![], vec!["out"]),
simple_node("B", vec!["in"], vec!["out"]),
simple_node("C", vec!["in"], vec![]),
];
let edges = vec![edge("A", "out", "B", "in"), edge("B", "out", "C", "in")];
Graph::new(nodes, edges)
}
pub fn branching_graph() -> Graph {
let nodes = vec![
simple_node("A", vec![], vec!["out1", "out2"]),
simple_node("B", vec!["in"], vec![]),
simple_node("C", vec!["in"], vec![]),
];
let edges = vec![edge("A", "out1", "B", "in"), edge("A", "out2", "C", "in")];
Graph::new(nodes, edges)
}
pub fn merging_graph() -> Graph {
let nodes = vec![
simple_node("A", vec![], vec!["out"]),
simple_node("B", vec![], vec!["out"]),
simple_node("C", vec!["in1", "in2"], vec![]),
];
let edges = vec![edge("A", "out", "C", "in1"), edge("B", "out", "C", "in2")];
Graph::new(nodes, edges)
}
pub fn diamond_graph() -> Graph {
let nodes = vec![
simple_node("A", vec![], vec!["out1", "out2"]),
simple_node("B", vec!["in"], vec!["out"]),
simple_node("C", vec!["in"], vec!["out"]),
simple_node("D", vec!["in1", "in2"], vec![]),
];
let edges = vec![
edge("A", "out1", "B", "in"),
edge("A", "out2", "C", "in"),
edge("B", "out", "D", "in1"),
edge("C", "out", "D", "in2"),
];
Graph::new(nodes, edges)
}
pub fn sink_graph() -> Graph {
let nodes = vec![
simple_node("A", vec![], vec!["out"]),
simple_node("B", vec!["in"], vec!["out"]), ];
let edges = vec![edge("A", "out", "B", "in")];
Graph::new(nodes, edges)
}
pub fn node_with_conditions(
name: &str,
in_ports: HashMap<PortName, Port>,
out_ports: HashMap<PortName, Port>,
in_salvo_conditions: IndexMap<String, SalvoCondition>,
out_salvo_conditions: IndexMap<String, SalvoCondition>,
) -> Node {
Node {
name: name.to_string(),
in_ports,
out_ports,
in_salvo_conditions,
out_salvo_conditions,
dependency_request_config: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linear_graph_creation() {
let graph = linear_graph_3();
assert_eq!(graph.nodes().len(), 3);
assert_eq!(graph.edges().len(), 2);
assert!(graph.validate().is_empty());
}
#[test]
fn test_branching_graph_creation() {
let graph = branching_graph();
assert_eq!(graph.nodes().len(), 3);
assert_eq!(graph.edges().len(), 2);
assert!(graph.validate().is_empty());
}
#[test]
fn test_merging_graph_creation() {
let graph = merging_graph();
assert_eq!(graph.nodes().len(), 3);
assert_eq!(graph.edges().len(), 2);
assert!(graph.validate().is_empty());
}
#[test]
fn test_diamond_graph_creation() {
let graph = diamond_graph();
assert_eq!(graph.nodes().len(), 4);
assert_eq!(graph.edges().len(), 4);
assert!(graph.validate().is_empty());
}
#[test]
fn test_sink_graph_creation() {
let graph = sink_graph();
assert_eq!(graph.nodes().len(), 2);
assert_eq!(graph.edges().len(), 1);
assert!(graph.validate().is_empty());
let node_b = graph.nodes().get("B").unwrap();
assert!(node_b.out_ports.contains_key("out"));
assert!(
graph
.get_edge_by_tail(&PortRef {
node_name: "B".to_string(),
port_type: PortType::Output,
port_name: "out".to_string(),
})
.is_none()
);
}
}