use std::cmp::Ordering;
use std::iter;
use crate::Language;
use super::builder::{DraftDefinition, DraftReference, DraftResolution, DraftUnit};
use super::definition::SymbolDefinition;
use super::error::ResolutionReportError;
use super::id::{DefinitionId, ReferenceId, ResolutionUnitId};
use super::record::{ResolutionCandidate, ResolutionRecord};
use super::reference::SymbolReference;
use super::report::{ResolutionReport, ResolutionTier};
use super::unit::ResolutionUnit;
use super::validate::validate;
pub(crate) fn assemble(
tier: ResolutionTier,
units: Vec<DraftUnit>,
definitions: Vec<DraftDefinition>,
references: Vec<DraftReference>,
resolutions: Vec<Option<DraftResolution>>,
) -> Result<ResolutionReport, ResolutionReportError> {
let languages: Box<[Language]> = units.iter().map(|unit| unit.language).collect();
let (units, unit_ids) = rank(units, |left, right| left.key.cmp(&right.key));
let (definitions, definition_ids) = rank(definitions, |left, right| {
definition_order(left, right, &unit_ids)
});
let (answered, _) = rank(pair(references, resolutions), |left, right| {
reference_order(&left.0, &right.0, &unit_ids)
});
let (references, resolutions): (Vec<DraftReference>, Vec<Option<DraftResolution>>) =
answered.into_iter().unzip();
let report = ResolutionReport::new(
tier,
sorted_units(units),
sorted_definitions(definitions, &languages, &unit_ids, &definition_ids),
sorted_references(references, &languages, &unit_ids, &definition_ids),
records(resolutions, &definition_ids)?,
);
validate(&report)?;
Ok(report)
}
fn pair(
references: Vec<DraftReference>,
resolutions: Vec<Option<DraftResolution>>,
) -> Vec<(DraftReference, Option<DraftResolution>)> {
references
.into_iter()
.zip(resolutions.into_iter().chain(iter::repeat_with(|| None)))
.collect()
}
fn rank<T>(items: Vec<T>, compare: impl Fn(&T, &T) -> Ordering) -> (Vec<T>, Box<[u32]>) {
let mut paired: Vec<(usize, T)> = items.into_iter().enumerate().collect();
paired.sort_by(|left, right| compare(&left.1, &right.1));
let mut positions = vec![0_u32; paired.len()];
let sorted = paired
.into_iter()
.enumerate()
.map(|(index, (local, item))| {
positions[local] = index_of(index);
item
})
.collect();
(sorted, positions.into_boxed_slice())
}
fn definition_order(left: &DraftDefinition, right: &DraftDefinition, unit_ids: &[u32]) -> Ordering {
let left = (
unit_ids[left.unit as usize],
&left.span,
left.kind,
&left.name,
);
let right = (
unit_ids[right.unit as usize],
&right.span,
right.kind,
&right.name,
);
left.cmp(&right)
}
fn reference_order(left: &DraftReference, right: &DraftReference, unit_ids: &[u32]) -> Ordering {
let left = (
unit_ids[left.unit as usize],
&left.span,
left.kind,
&left.text,
);
let right = (
unit_ids[right.unit as usize],
&right.span,
right.kind,
&right.text,
);
left.cmp(&right)
}
fn sorted_units(units: Vec<DraftUnit>) -> Box<[ResolutionUnit]> {
units
.into_iter()
.enumerate()
.map(|(index, draft)| {
ResolutionUnit::new(
ResolutionUnitId::new(index_of(index)),
draft.language,
draft.key,
draft.name,
)
})
.collect()
}
fn sorted_definitions(
definitions: Vec<DraftDefinition>,
languages: &[Language],
unit_ids: &[u32],
definition_ids: &[u32],
) -> Box<[SymbolDefinition]> {
definitions
.into_iter()
.enumerate()
.map(|(index, draft)| {
let unit = draft.unit as usize;
SymbolDefinition::new(
DefinitionId::new(index_of(index)),
ResolutionUnitId::new(unit_ids[unit]),
languages[unit],
draft.kind,
draft.name,
draft.span,
draft
.parent
.map(|parent| DefinitionId::new(definition_ids[parent as usize])),
)
})
.collect()
}
fn sorted_references(
references: Vec<DraftReference>,
languages: &[Language],
unit_ids: &[u32],
definition_ids: &[u32],
) -> Box<[SymbolReference]> {
references
.into_iter()
.enumerate()
.map(|(index, draft)| {
let unit = draft.unit as usize;
SymbolReference::new(
ReferenceId::new(index_of(index)),
ResolutionUnitId::new(unit_ids[unit]),
languages[unit],
draft.kind,
draft.text,
draft.span,
draft
.enclosing
.map(|enclosing| DefinitionId::new(definition_ids[enclosing as usize])),
)
})
.collect()
}
fn records(
resolutions: Vec<Option<DraftResolution>>,
definition_ids: &[u32],
) -> Result<Box<[ResolutionRecord]>, ResolutionReportError> {
resolutions
.into_iter()
.enumerate()
.map(|(index, stated)| {
let reference = index_of(index);
let stated = stated.ok_or(ResolutionReportError::MissingResolution { reference })?;
Ok(record(reference, stated, definition_ids))
})
.collect()
}
fn record(reference: u32, stated: DraftResolution, definition_ids: &[u32]) -> ResolutionRecord {
let mut candidates: Vec<ResolutionCandidate> = stated
.candidates
.into_vec()
.into_iter()
.map(|(definition, certainty)| {
let definition = DefinitionId::new(definition_ids[definition as usize]);
ResolutionCandidate::new(definition, certainty)
})
.collect();
candidates.sort_by_key(ResolutionCandidate::definition);
let mut gaps = stated.gaps;
gaps.sort_unstable();
let candidates = candidates.into_boxed_slice();
ResolutionRecord::new(ReferenceId::new(reference), candidates, gaps)
}
fn index_of(value: usize) -> u32 {
u32::try_from(value).unwrap_or(u32::MAX)
}