use std::collections::{BTreeMap, btree_map};
use std::sync::Arc;
use pedant_types::{
ResolutionRecord, ResolutionReport, SourceSpan, SymbolDefinition, SymbolKind, SymbolReference,
};
use crate::error::GraphBuildError;
use crate::id::{index_of, position};
use super::draft::{FragmentSlot, UnitPlan};
use super::validation;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct SourceIdentity {
unit: Arc<str>,
path: Arc<str>,
}
impl SourceIdentity {
pub(crate) fn new(unit: Arc<str>, path: Arc<str>) -> Self {
Self { unit, path }
}
pub(crate) fn shared(&self) -> Self {
Self {
unit: Arc::clone(&self.unit),
path: Arc::clone(&self.path),
}
}
pub(crate) fn path(&self) -> &Arc<str> {
&self.path
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct DefinitionIdentity {
source: SourceIdentity,
span: SourceSpan,
kind: SymbolKind,
name: Arc<str>,
occurrence: u32,
}
impl DefinitionIdentity {
fn stated(source: SourceIdentity, definition: &SymbolDefinition) -> Self {
Self {
source,
span: SymbolDefinition::span(definition).clone(),
kind: definition.kind(),
name: Arc::from(definition.name()),
occurrence: 0,
}
}
fn at(&self, occurrence: u32) -> Self {
Self {
source: self.source.shared(),
span: self.span.clone(),
kind: self.kind,
name: Arc::clone(&self.name),
occurrence,
}
}
pub(crate) fn source(&self) -> &SourceIdentity {
&self.source
}
pub(crate) fn span(&self) -> &SourceSpan {
&self.span
}
pub(crate) fn name(&self) -> &Arc<str> {
&self.name
}
}
struct PlacedDefinition {
fragment: u32,
identity: Arc<DefinitionIdentity>,
}
pub(crate) struct DefinitionTable {
placed: Box<[PlacedDefinition]>,
}
impl DefinitionTable {
pub(crate) fn new(
report: &ResolutionReport,
sources: &SourceSet,
) -> Result<Self, GraphBuildError> {
let mut occurrences: BTreeMap<DefinitionIdentity, u32> = BTreeMap::new();
let mut placed = Vec::with_capacity(report.definitions().len());
for definition in report.definitions() {
let reported = definition.unit().index();
let file = SymbolDefinition::span(definition).file();
let (fragment, source) = validation::instantiated_source(sources, reported, file)?;
let stated = DefinitionIdentity::stated(source.shared(), definition);
placed.push(PlacedDefinition {
fragment,
identity: Arc::new(counted(occurrences.entry(stated))),
});
}
Ok(Self {
placed: placed.into_boxed_slice(),
})
}
pub(crate) fn identity(&self, definition: u32) -> Option<&Arc<DefinitionIdentity>> {
self.placed
.get(index_of(definition))
.map(|placed| &placed.identity)
}
pub(crate) fn fragment(&self, definition: u32) -> Option<u32> {
self.placed
.get(index_of(definition))
.map(|placed| placed.fragment)
}
}
fn counted(held: btree_map::Entry<'_, DefinitionIdentity, u32>) -> DefinitionIdentity {
match held {
btree_map::Entry::Vacant(slot) => {
let identity = slot.key().at(0);
slot.insert(1);
identity
}
btree_map::Entry::Occupied(mut held) => {
let occurrence = *held.get();
let identity = held.key().at(occurrence);
*held.get_mut() = occurrence.saturating_add(1);
identity
}
}
}
pub(crate) struct PlacedSource {
pub(crate) unit: u32,
pub(crate) source: SourceIdentity,
}
pub(crate) struct SourceSet {
units: Box<[UnitPlan]>,
located: Box<[BTreeMap<Arc<str>, u32>]>,
placed: Box<[PlacedSource]>,
}
impl SourceSet {
pub(crate) fn new(units: Box<[UnitPlan]>) -> Result<Self, GraphBuildError> {
let stated = units.iter().map(|unit| unit.sources.len()).sum();
let mut placed: Vec<PlacedSource> = Vec::with_capacity(stated);
let located: Vec<BTreeMap<Arc<str>, u32>> = units
.iter()
.enumerate()
.map(|(index, unit)| unit_sources(&mut placed, position(index), unit))
.collect::<Result<_, GraphBuildError>>()?;
Ok(Self {
units,
located: located.into_boxed_slice(),
placed: placed.into_boxed_slice(),
})
}
pub(crate) fn units(&self) -> &[UnitPlan] {
&self.units
}
pub(crate) fn placed(&self) -> &[PlacedSource] {
&self.placed
}
pub(crate) fn locate(&self, unit: u32, path: &str) -> Option<(u32, &SourceIdentity)> {
let scope = self.located.get(index_of(unit))?;
let source = *scope.get(path)?;
let placed = self.placed.get(index_of(source))?;
Some((source, &placed.source))
}
pub(crate) fn finish(self) -> Box<[UnitPlan]> {
self.units
}
}
#[derive(Default)]
pub(crate) struct SourceRecords<'a> {
pub(crate) definitions: Vec<(u32, &'a SymbolDefinition)>,
pub(crate) references: Vec<(&'a SymbolReference, &'a ResolutionRecord)>,
}
pub(crate) struct RecordPlacement<'a> {
pub(crate) definitions: Box<[FragmentSlot]>,
pub(crate) references: Box<[FragmentSlot]>,
pub(crate) stated: Box<[SourceRecords<'a>]>,
}
impl<'a> RecordPlacement<'a> {
pub(crate) fn stated(
report: &'a ResolutionReport,
resolved: &[(&'a SymbolReference, &'a ResolutionRecord)],
placed: (&SourceSet, &DefinitionTable),
) -> Result<Self, GraphBuildError> {
let (sources, table) = placed;
let mut stated: Box<[SourceRecords<'a>]> = sources
.placed()
.iter()
.map(|_| SourceRecords::default())
.collect();
let definitions = place_definitions(report, table, &mut stated)?;
let references = place_references(resolved, sources, &mut stated)?;
Ok(Self {
definitions,
references,
stated,
})
}
}
fn place_definitions<'a>(
report: &'a ResolutionReport,
table: &DefinitionTable,
stated: &mut [SourceRecords<'a>],
) -> Result<Box<[FragmentSlot]>, GraphBuildError> {
let mut placed = Vec::with_capacity(report.definitions().len());
for (index, definition) in report.definitions().iter().enumerate() {
let at = position(index);
let fragment = validation::definition_fragment(table, at)?;
let identity = validation::definition_identity(table, at)?;
let held = stated
.get_mut(index_of(fragment))
.map(|records| &mut records.definitions);
placed.push(validation::placed_slot(
placed_record(held, fragment, (at, definition)),
definition.unit().index(),
identity.source().path(),
)?);
}
Ok(placed.into_boxed_slice())
}
fn place_references<'a>(
resolved: &[(&'a SymbolReference, &'a ResolutionRecord)],
sources: &SourceSet,
stated: &mut [SourceRecords<'a>],
) -> Result<Box<[FragmentSlot]>, GraphBuildError> {
let mut placed = Vec::with_capacity(resolved.len());
for (reference, record) in resolved.iter().copied() {
let reported = reference.unit().index();
let file = SymbolReference::span(reference).file();
let (fragment, source) = validation::instantiated_source(sources, reported, file)?;
let held = stated
.get_mut(index_of(fragment))
.map(|records| &mut records.references);
placed.push(validation::placed_slot(
placed_record(held, fragment, (reference, record)),
reported,
source.path(),
)?);
}
Ok(placed.into_boxed_slice())
}
fn placed_record<T>(stated: Option<&mut Vec<T>>, fragment: u32, record: T) -> Option<FragmentSlot> {
let held = stated?;
let slot = position(held.len());
held.push(record);
Some(FragmentSlot { fragment, slot })
}
fn unit_sources(
placed: &mut Vec<PlacedSource>,
unit: u32,
plan: &UnitPlan,
) -> Result<BTreeMap<Arc<str>, u32>, GraphBuildError> {
let mut located = BTreeMap::new();
for path in &plan.sources {
let at = position(placed.len());
validation::distinct_source(located.insert(Arc::clone(path), at), unit, path)?;
placed.push(PlacedSource {
unit,
source: SourceIdentity::new(Arc::clone(&plan.key), Arc::clone(path)),
});
}
Ok(located)
}