use crate::graph;
use super::index::{def_files, GraphIndex, InboundEntry, OutboundEntry, SymbolRefs};
use super::refs::project_of;
use super::refs_tables::{loc_cmp, InboundRow, Table};
use super::seq::SeqSet;
use std::collections::HashMap;
pub(super) fn implemented_interfaces(index: &GraphIndex, def_id: &str) -> Vec<String> {
let mut seen: SeqSet<String> = SeqSet::new();
for file in def_files(index, def_id) {
let Some(o) = index.outbound_by_file.get(&file) else {
continue;
};
for &ei in o.inherits.iter().chain(o.implements.iter()) {
let to = match &index.graph.edges[ei] {
graph::Edge::Inherits { to, .. } | graph::Edge::Implements { to, .. } => to,
_ => continue,
};
if seen.contains(to) {
continue;
}
if index.def(to).map(|d| d.kind.as_str()) != Some("interface") {
continue;
}
seen.insert(to.clone());
}
}
seen.into_vec()
}
pub(super) fn inbound_walk_kinds(inb: &InboundEntry) -> [&Vec<usize>; 5] {
[
&inb.inherits,
&inb.uses_type,
&inb.uses_member,
&inb.implements,
&inb.overrides,
]
}
pub(super) fn record_dispatch_edge(
e: &graph::Edge,
i: usize,
from_file: &str,
to: &str,
outbound_by_file: &mut HashMap<String, OutboundEntry>,
inbound: &mut HashMap<String, InboundEntry>,
) {
let out_entry = outbound_by_file.entry(from_file.to_string()).or_default();
let in_entry = inbound.entry(to.to_string()).or_default();
match e {
graph::Edge::Implements { .. } => {
out_entry.implements.push(i);
in_entry.implements.push(i);
}
graph::Edge::Overrides { .. } => {
out_entry.overrides.push(i);
in_entry.overrides.push(i);
}
_ => unreachable!("record_dispatch_edge only ever receives Implements/Overrides edges"),
}
}
pub(super) fn member_dispatch_edges(
refs: &SymbolRefs,
edges: &[graph::Edge],
name: &str,
) -> Vec<usize> {
let named = |e: usize| matches!(&edges[e], graph::Edge::Implements { member, .. } | graph::Edge::Overrides { member, .. } if member.as_deref() == Some(name));
let mut kept: Vec<usize> = refs
.inbound_implements
.iter()
.chain(refs.inbound_overrides.iter())
.copied()
.filter(|&e| named(e))
.collect();
kept.sort_by(|&a, &b| loc_cmp(&edges[a], &edges[b]));
kept
}
pub(super) fn dispatch_table(rows: Vec<InboundRow>) -> Table<InboundRow> {
Table {
total: rows.len(),
dropped: 0,
rows,
}
}
pub(super) fn split_dispatch_rows(
edges: &[graph::Edge],
dispatch: &[usize],
mut row_of: impl FnMut(usize) -> InboundRow,
) -> (Vec<InboundRow>, Vec<InboundRow>) {
let mut implements_rows = Vec::new();
let mut overrides_rows = Vec::new();
for &e in dispatch {
let row = row_of(e);
match &edges[e] {
graph::Edge::Implements { .. } => implements_rows.push(row),
graph::Edge::Overrides { .. } => overrides_rows.push(row),
_ => unreachable!(
"member_dispatch_edges only ever returns implements/overrides edge indices"
),
}
}
(implements_rows, overrides_rows)
}
pub(super) const K_INHERITS: usize = 0;
pub(super) const K_USES_TYPE: usize = 1;
pub(super) const K_USES_MEMBER: usize = 2;
pub(super) const K_IMPLEMENTS: usize = 3;
pub(super) const K_OVERRIDES: usize = 4;
pub(super) const K_IMPORTS: usize = 5;
pub(super) struct RankedOutbound {
pub kind: usize,
pub edge: usize,
pub heuristic: bool,
}
pub(super) fn outbound_foreign(
def_project: &str,
edges: &[graph::Edge],
r: &RankedOutbound,
) -> usize {
if r.kind == K_IMPORTS {
return 1;
}
let to_file = match &edges[r.edge] {
graph::Edge::Inherits { to_file, .. }
| graph::Edge::UsesType { to_file, .. }
| graph::Edge::UsesMember { to_file, .. }
| graph::Edge::Implements { to_file, .. }
| graph::Edge::Overrides { to_file, .. } => to_file.as_str(),
_ => unreachable!(
"outbound ranked kinds other than imports only ever hold inherits/uses-type/uses-member/implements/overrides edge indices"
),
};
usize::from(project_of(to_file) != def_project)
}
pub(super) fn push_ranked(
ranked: &mut Vec<RankedOutbound>,
kind: usize,
precise: &[usize],
heuristic: &[usize],
) {
for &e in precise {
ranked.push(RankedOutbound {
kind,
edge: e,
heuristic: false,
});
}
for &e in heuristic {
ranked.push(RankedOutbound {
kind,
edge: e,
heuristic: true,
});
}
}