use std::collections::{HashMap, HashSet};
use crate::graph;
use super::index::{def_files, GraphIndex, InboundEntry, OutboundEntry};
use super::refs_tables::{cap_rows, loc_cmp, Table};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusDirection {
In,
Out,
}
impl BusDirection {
pub fn as_str(self) -> &'static str {
match self {
BusDirection::In => "in",
BusDirection::Out => "out",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BusHopRow {
pub file: String,
pub line: usize,
pub message: String,
pub to: String,
pub to_file: String,
pub evidence: String,
pub direction: BusDirection,
pub message_handlers: usize,
}
fn handlers_by_message(edges: &[graph::Edge]) -> HashMap<&str, HashSet<&str>> {
let mut out: HashMap<&str, HashSet<&str>> = HashMap::new();
for e in edges {
if let graph::Edge::BusHop { message, to, .. } = e {
out.entry(message.as_str()).or_default().insert(to.as_str());
}
}
out
}
fn bus_hop_row(
e: &graph::Edge,
direction: BusDirection,
handlers: &HashMap<&str, HashSet<&str>>,
) -> BusHopRow {
let graph::Edge::BusHop {
from_file,
from_line,
message,
to,
to_file,
evidence,
} = e
else {
unreachable!("bus_hop_row is only ever called with a BusHop edge index");
};
BusHopRow {
file: from_file.clone(),
line: *from_line,
message: message.clone(),
to: to.clone(),
to_file: to_file.clone(),
evidence: evidence.clone(),
direction,
message_handlers: handlers.get(message.as_str()).map_or(0, HashSet::len),
}
}
pub(super) fn record_bus_edge(
i: usize,
from_file: &str,
to: &str,
outbound_by_file: &mut HashMap<String, OutboundEntry>,
inbound: &mut HashMap<String, InboundEntry>,
) {
outbound_by_file
.entry(from_file.to_string())
.or_default()
.bus_hop
.push(i);
inbound.entry(to.to_string()).or_default().bus_hop.push(i);
}
pub(super) fn empty_bus_table() -> Table<BusHopRow> {
Table {
total: 0,
dropped: 0,
rows: Vec::new(),
}
}
pub(super) fn symbol_bus_rows(index: &GraphIndex, def_id: &str, cap: usize) -> Table<BusHopRow> {
let edges = &index.graph.edges;
let mut inbound: Vec<usize> = index
.inbound
.get(def_id)
.map(|e| e.bus_hop.clone())
.unwrap_or_default();
inbound.sort_by(|&a, &b| loc_cmp(&edges[a], &edges[b]));
let mut outbound: Vec<usize> = Vec::new();
for file in def_files(index, def_id) {
if let Some(o) = index.outbound_by_file.get(&file) {
outbound.extend(o.bus_hop.iter().copied());
}
}
outbound.sort_by(|&a, &b| loc_cmp(&edges[a], &edges[b]));
let handlers = handlers_by_message(edges);
let combined: Vec<BusHopRow> = inbound
.iter()
.map(|&e| bus_hop_row(&edges[e], BusDirection::In, &handlers))
.chain(
outbound
.iter()
.map(|&e| bus_hop_row(&edges[e], BusDirection::Out, &handlers)),
)
.collect();
let total = combined.len();
let (rows, dropped) = cap_rows(combined, cap);
Table {
total,
dropped,
rows,
}
}