use super::anchor_index::is_anchor_target_capability;
use super::pub_fns::PubFnInfo;
use super::workspace_graph::{canonical_name_for_pub_fn, CallGraph};
use super::HandlerTouchpoints;
use crate::adapters::analyzers::architecture::compiled::CompiledCallParity;
use std::collections::{HashMap, HashSet, VecDeque};
pub(super) type AdapterCoverage = HashMap<String, HashSet<String>>;
pub(super) fn build_adapter_coverage(
pub_fns_by_layer: &HashMap<String, Vec<PubFnInfo<'_>>>,
touchpoints: &HandlerTouchpoints,
cp: &CompiledCallParity,
) -> AdapterCoverage {
let mut coverage: AdapterCoverage = HashMap::new();
for adapter in &cp.adapters {
let mut union: HashSet<String> = HashSet::new();
if let Some(handlers) = pub_fns_by_layer.get(adapter) {
for info in handlers {
let canonical = canonical_name_for_pub_fn(info);
if let Some(tps) = touchpoints.get(&canonical) {
union.extend(tps.iter().cloned());
}
}
}
coverage.insert(adapter.clone(), union);
}
coverage
}
pub(super) fn build_adapter_reachable_targets(
coverage: &AdapterCoverage,
graph: &CallGraph,
target_layer: &str,
adapter_layers: &[String],
) -> HashSet<String> {
let mut reachable: HashSet<String> = HashSet::new();
let mut queue: VecDeque<String> = VecDeque::new();
for tps in coverage.values() {
for tp in tps {
if reachable.insert(tp.clone()) {
queue.push_back(tp.clone());
}
}
}
while let Some(node) = queue.pop_front() {
let Some(callees) = graph.forward.get(&node) else {
continue;
};
for callee in callees {
if is_target_capability_node(callee, graph, target_layer, adapter_layers)
&& reachable.insert(callee.clone())
{
queue.push_back(callee.clone());
}
}
}
reachable
}
fn is_target_capability_node(
canonical: &str,
graph: &CallGraph,
target_layer: &str,
adapter_layers: &[String],
) -> bool {
if graph.layer_of(canonical) == Some(target_layer) && graph.forward.contains_key(canonical) {
return true;
}
graph
.trait_method_anchors
.get(canonical)
.is_some_and(|info| is_anchor_target_capability(info, target_layer, adapter_layers))
}