use std::collections::{HashMap, HashSet};
use super::model::{DefRow, EdgeRow, OracleRef, Tier, Unit};
pub fn is_unjudged_discovered_edge(
e: &EdgeRow,
discovered_shapes: &HashMap<(String, usize, String), String>,
) -> bool {
if e.tier != Tier::SemanticDiscovered {
return false;
}
let Some(member) = &e.member else {
return false;
};
discovered_shapes
.get(&(e.from_file.clone(), e.from_line, member.clone()))
.is_some_and(|shape| shape == "identifier")
}
pub fn target_matches(r: &OracleRef, edge_to: &str) -> bool {
let Some(t) = &r.target else { return false };
if t == edge_to {
return true;
}
if r.target_kind.as_deref() == Some("enum-member") {
if let Some((prefix, _)) = t.rsplit_once('.') {
if prefix == edge_to {
return true;
}
}
}
false
}
pub fn member_matches(edge_member: &Option<String>, record_member: &str) -> bool {
match edge_member {
Some(m) => m == record_member,
None => true,
}
}
pub fn target_known(defs_by_id: &HashMap<&str, &DefRow>, r: &OracleRef) -> bool {
let Some(t) = &r.target else { return false };
if defs_by_id.contains_key(t.as_str()) {
return true;
}
if r.target_kind.as_deref() == Some("enum-member") {
if let Some((prefix, _)) = t.rsplit_once('.') {
if defs_by_id.contains_key(prefix) {
return true;
}
}
}
false
}
pub fn short_name(id: &str) -> &str {
id.rsplit(['.', '+']).next().unwrap_or(id)
}
pub fn file_to_unit(units: &[Unit]) -> HashMap<String, String> {
let mut m = HashMap::new();
for u in units {
for f in &u.files {
m.entry(f.clone()).or_insert_with(|| u.name.clone());
}
}
m
}
pub fn reach(units: &[Unit]) -> HashMap<String, HashSet<String>> {
let by_name: HashMap<&str, &Unit> = units.iter().map(|u| (u.name.as_str(), u)).collect();
let mut result = HashMap::new();
for u in units {
let mut seen: HashSet<String> = HashSet::new();
let mut stack = vec![u.name.clone()];
while let Some(n) = stack.pop() {
if seen.contains(&n) {
continue;
}
seen.insert(n.clone());
if let Some(unit) = by_name.get(n.as_str()) {
for r in &unit.refs {
if !seen.contains(r) {
stack.push(r.clone());
}
}
}
}
result.insert(u.name.clone(), seen);
}
result
}
pub fn is_structural(
e: &EdgeRow,
units_method: bool,
file_unit: &HashMap<String, String>,
reach_map: &HashMap<String, HashSet<String>>,
test_by_id: &HashMap<String, bool>,
test_files: &HashSet<String>,
) -> Option<bool> {
if units_method {
let u1 = file_unit.get(&e.from_file)?;
let u2 = file_unit.get(&e.to_file)?;
let reachable = reach_map.get(u1).is_some_and(|s| s.contains(u2));
Some(!reachable)
} else {
let target_test = *test_by_id.get(&e.to)?;
if !target_test {
return Some(false);
}
Some(!test_files.contains(&e.from_file))
}
}