use std::collections::HashMap;
use crate::graph;
use super::index::{def_files, symbol_refs, GraphIndex};
use super::member::{self, MemberCandidate, MemberSeedResolution};
use super::refs_tables::{edge_loc, row_tier};
use super::symbol::{resolve_symbol, Resolution};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TestVia {
Attribute,
Project,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestRow {
pub file: String,
pub test_defs: Vec<String>,
pub lines: Vec<usize>,
pub ref_count: usize,
pub heuristic: bool,
pub tier: Option<graph::HeuristicTier>,
pub via: TestVia,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TestsModel {
pub query: String,
pub symbol: String,
pub def_files: Vec<String>,
pub rows: Vec<TestRow>,
pub test_file_count: usize,
pub ref_count: usize,
pub heuristic_file_count: usize,
pub heuristic_ref_count: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TestsResult {
Resolved(TestsModel),
Ambiguous(Vec<String>),
MemberAmbiguous(Vec<MemberCandidate>),
NotFound,
}
fn collect_test_rows(index: &GraphIndex, kinds: [&[usize]; 5], heuristic: bool) -> Vec<TestRow> {
let mut rows: Vec<TestRow> = Vec::new();
let mut by_file: HashMap<String, usize> = HashMap::new();
for idxs in kinds {
for &i in idxs {
let (from_file, from_line) = edge_loc(&index.graph.edges[i]);
let test_defs = index.test_defs_by_file.get(from_file);
let via = if test_defs.is_some() {
TestVia::Attribute
} else if index.is_test_file(from_file) {
TestVia::Project
} else {
continue;
};
let slot = match by_file.get(from_file) {
Some(&slot) => slot,
None => {
let slot = rows.len();
rows.push(TestRow {
file: from_file.to_string(),
test_defs: test_defs
.map(|defs| {
defs.iter()
.map(|&d| index.graph.defs[d].id.clone())
.collect()
})
.unwrap_or_default(),
lines: Vec::new(),
ref_count: 0,
heuristic,
tier: row_tier(heuristic, false),
via,
});
by_file.insert(from_file.to_string(), slot);
slot
}
};
if index.graph.edges[i].tier() == Some(graph::HeuristicTier::Ext) {
rows[slot].tier = row_tier(heuristic, true);
}
rows[slot].lines.push(from_line);
rows[slot].ref_count += 1;
}
}
for row in &mut rows {
row.lines.sort_unstable();
}
rows
}
pub fn build_tests_model(index: &GraphIndex, query: &str) -> TestsResult {
let id = match resolve_symbol(index, query) {
Resolution::Resolved(id) => id,
Resolution::Ambiguous(ids) => return TestsResult::Ambiguous(ids),
Resolution::NotFound => match member::resolve_member_seed(index, query) {
MemberSeedResolution::Resolved(id) => id,
MemberSeedResolution::Ambiguous(candidates) => {
return TestsResult::MemberAmbiguous(candidates)
}
MemberSeedResolution::NotFound => return TestsResult::NotFound,
},
};
let refs = symbol_refs(index, &id);
let precise = collect_test_rows(
index,
[
&refs.inbound_inherits,
&refs.inbound_uses_type,
&refs.inbound_uses_member,
&refs.inbound_implements,
&refs.inbound_overrides,
],
false,
);
const EMPTY: &[usize] = &[];
let heuristic = collect_test_rows(
index,
[
&refs.heuristic_inbound_inherits,
&refs.heuristic_inbound_uses_type,
&refs.heuristic_inbound_uses_member,
EMPTY,
EMPTY,
],
true,
);
let test_file_count = precise.len();
let ref_count: usize = precise.iter().map(|r| r.ref_count).sum();
let heuristic_file_count = heuristic.len();
let heuristic_ref_count: usize = heuristic.iter().map(|r| r.ref_count).sum();
let mut rows = precise;
rows.extend(heuristic);
TestsResult::Resolved(TestsModel {
query: query.to_string(),
symbol: id.clone(),
def_files: def_files(index, &id),
rows,
test_file_count,
ref_count,
heuristic_file_count,
heuristic_ref_count,
})
}