use super::{ApiEvidence, ByteRange, Resolution, TypeEvidence, TypeTag, Unit};
pub(super) fn token_count_meets_minimum(token_count: usize, minimum: u32) -> bool {
u32::try_from(token_count).map_or(true, |count| count >= minimum)
}
pub(super) fn unit_meets_minimum(unit: &Unit, minimum: u32) -> bool {
token_count_meets_minimum(unit.tokens.1.saturating_sub(unit.tokens.0), minimum)
}
#[derive(Debug, Clone, Default)]
pub struct ResolvedTypes {
types: Vec<Vec<(ByteRange, TypeTag)>>,
apis: Vec<Vec<(ByteRange, String)>>,
names: Vec<Resolution>,
}
impl ResolvedTypes {
#[must_use]
pub fn per_file(mut per_file: Vec<Vec<(ByteRange, TypeTag)>>) -> Self {
for file in &mut per_file {
file.sort_by_key(|(range, _)| (range.start, range.end));
}
Self {
types: per_file,
apis: Vec::new(),
names: Vec::new(),
}
}
#[must_use]
pub fn per_file_with_apis(
mut per_file: Vec<Vec<(ByteRange, TypeTag)>>,
mut apis_per_file: Vec<Vec<(ByteRange, String)>>,
) -> Self {
for file in &mut per_file {
file.sort_by_key(|(range, _)| (range.start, range.end));
}
for file in &mut apis_per_file {
file.sort_by_key(|(range, _)| (range.start, range.end));
}
Self {
types: per_file,
apis: apis_per_file,
names: Vec::new(),
}
}
#[must_use]
pub fn per_file_with_semantic_normalization(
per_file: Vec<Vec<(ByteRange, TypeTag)>>,
apis_per_file: Vec<Vec<(ByteRange, String)>>,
names_per_file: Vec<Resolution>,
) -> Self {
let mut resolved = Self::per_file_with_apis(per_file, apis_per_file);
resolved.names = names_per_file;
resolved
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.types.iter().all(Vec::is_empty)
&& self.apis.iter().all(Vec::is_empty)
&& self.names.iter().all(Resolution::is_empty)
}
pub(super) fn names_for(&self, file: usize) -> Option<&Resolution> {
self.names.get(file).filter(|names| !names.is_empty())
}
pub(super) fn within(&self, unit: &Unit) -> Option<TypeEvidence> {
let file = self.types.get(unit.file)?;
let from = file.partition_point(|(range, _)| range.start < unit.range.start);
let tags = file[from..]
.iter()
.take_while(|(range, _)| range.start < unit.range.end)
.filter(|(range, _)| range.end <= unit.range.end)
.map(|(_, tag)| *tag);
let evidence = TypeEvidence::from_tags(tags);
(!evidence.is_empty()).then_some(evidence)
}
pub(super) fn apis_within(&self, unit: &Unit) -> Option<ApiEvidence> {
let file = self.apis.get(unit.file)?;
let from = file.partition_point(|(range, _)| range.start < unit.range.start);
let targets = file[from..]
.iter()
.take_while(|(range, _)| range.start < unit.range.end)
.filter(|(range, _)| range.end <= unit.range.end)
.map(|(_, target)| target.clone());
let evidence = ApiEvidence::from_targets(targets);
(!evidence.is_empty()).then_some(evidence)
}
}
pub(super) struct UnitEvidence {
pub(super) types: Vec<Option<TypeEvidence>>,
pub(super) apis: Vec<Option<ApiEvidence>>,
}
pub(super) fn unit_evidence(units: &[Unit], resolved: &ResolvedTypes) -> UnitEvidence {
UnitEvidence {
types: units.iter().map(|unit| resolved.within(unit)).collect(),
apis: units
.iter()
.map(|unit| resolved.apis_within(unit))
.collect(),
}
}