use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_core::resolution::rust::{
RustResolutionSnapshot, RustSnapshotUnitId, RustTargetResolution,
};
use pedant_types::{ResolutionRecord, ResolutionReport, SymbolReference};
use crate::edge::DependencyEvidence;
use crate::error::GraphBuildError;
use crate::graph::CodeGraph;
use crate::id::{index_of, position};
use crate::limits::GraphLimits;
use super::assembly;
use super::claim::SourceKey;
use super::fragment::{
DependencyProjection, FragmentSlot, PlacedFragment, ProjectionPlan, SourceFragment, SourceSet,
UnitPlan,
};
use super::identity::DefinitionTable;
use super::index::ProjectionCapacity;
use super::mapping::{self, Vocabulary};
use super::reuse::ProjectionStore;
use super::source::{self, SourceRecords, StatedSource};
use super::validation;
struct Inputs<'a> {
snapshot: &'a RustResolutionSnapshot,
report: &'a ResolutionReport,
vocabulary: Vocabulary,
resolved: Box<[(&'a SymbolReference, &'a ResolutionRecord)]>,
}
struct PlannedUnits {
units: Vec<UnitPlan>,
bound: BTreeMap<RustSnapshotUnitId, u32>,
}
struct Placement<'a> {
definitions: Box<[FragmentSlot]>,
references: Box<[FragmentSlot]>,
stated: Box<[SourceRecords<'a>]>,
}
pub(crate) fn project(
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
limits: GraphLimits,
) -> Result<CodeGraph, GraphBuildError> {
validate(snapshot, resolution)?;
let planned = plan(snapshot, resolution, &mut ProjectionStore::unretained())?;
assembly::assemble(&planned, limits)
}
pub(crate) fn validate(
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
) -> Result<(), GraphBuildError> {
validation::check_root_target(snapshot, resolution)?;
validation::check_snapshot_identity(snapshot, resolution)
}
pub(crate) fn plan(
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
reuse: &mut ProjectionStore,
) -> Result<ProjectionPlan, GraphBuildError> {
let report = resolution.report();
let inputs = Inputs {
snapshot,
report,
vocabulary: Vocabulary::new(),
resolved: validation::resolved_references(report)?,
};
let planned = plan_units(&inputs, resolution)?;
let sources = SourceSet::new(planned.units.into_boxed_slice())?;
let table = DefinitionTable::new(report, &sources)?;
let placement = Placement::stated(&inputs, (&sources, &table))?;
let fragments = plan_fragments(&inputs, (&sources, &table, &placement), reuse)?;
let dependencies = plan_dependencies(&inputs, &planned.bound)?;
let capacity = capacity(&inputs);
Ok(ProjectionPlan {
tier: report.tier(),
units: sources.finish(),
fragments,
definitions: placement.definitions,
references: placement.references,
dependencies,
capacity,
})
}
fn capacity(inputs: &Inputs<'_>) -> ProjectionCapacity {
ProjectionCapacity {
units: inputs.report.units().len(),
definitions: inputs.report.definitions().len(),
references: inputs.resolved.len(),
edges: inputs
.resolved
.iter()
.fold(inputs.snapshot.edges().len(), |total, (_, record)| {
total.saturating_add(record.candidates().len())
}),
}
}
fn plan_units(
inputs: &Inputs<'_>,
resolution: &RustTargetResolution,
) -> Result<PlannedUnits, GraphBuildError> {
let mut planned = PlannedUnits {
units: Vec::with_capacity(inputs.report.units().len()),
bound: BTreeMap::new(),
};
for unit in inputs.report.units() {
let reported = unit.id().index();
let snapshot_unit = validation::stated_binding(resolution, unit)?;
let instance = validation::snapshot_instance(inputs.snapshot, (snapshot_unit, reported))?;
validation::distinct_binding(planned.bound.get(&snapshot_unit).copied(), reported)?;
planned.bound.insert(snapshot_unit, reported);
planned.units.push(UnitPlan {
key: Arc::from(unit.key()),
language: unit.language(),
name: Arc::from(unit.name()),
kind: inputs.vocabulary.unit_root(instance.kind()),
sources: instance.sources().into(),
});
}
Ok(planned)
}
impl<'a> Placement<'a> {
fn stated(
inputs: &Inputs<'a>,
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(inputs, table, &mut stated)?;
let references = place_references(inputs, sources, &mut stated)?;
Ok(Self {
definitions,
references,
stated,
})
}
}
fn place_definitions<'a>(
inputs: &Inputs<'a>,
table: &DefinitionTable,
stated: &mut [SourceRecords<'a>],
) -> Result<Box<[FragmentSlot]>, GraphBuildError> {
let mut placed = Vec::with_capacity(inputs.report.definitions().len());
for (index, definition) in inputs.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>(
inputs: &Inputs<'a>,
sources: &SourceSet,
stated: &mut [SourceRecords<'a>],
) -> Result<Box<[FragmentSlot]>, GraphBuildError> {
let mut placed = Vec::with_capacity(inputs.resolved.len());
for (reference, record) in inputs.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 plan_fragments<'a>(
inputs: &Inputs<'a>,
placed: (&SourceSet, &DefinitionTable, &Placement<'a>),
reuse: &mut ProjectionStore,
) -> Result<Box<[PlacedFragment]>, GraphBuildError> {
let (sources, table, placement) = placed;
let mut fragments = Vec::with_capacity(sources.placed().len());
for (source, records) in sources.placed().iter().zip(placement.stated.iter()) {
let stated = StatedSource {
table,
vocabulary: &inputs.vocabulary,
placed: source,
records,
};
fragments.push(placed_fragment(&stated, (inputs, sources.units()), reuse)?);
}
Ok(fragments.into_boxed_slice())
}
fn placed_fragment(
stated: &StatedSource<'_>,
planned: (&Inputs<'_>, &[UnitPlan]),
reuse: &mut ProjectionStore,
) -> Result<PlacedFragment, GraphBuildError> {
let (inputs, units) = planned;
let placed = stated.placed;
let key = SourceKey::stated(
inputs.snapshot,
(units, placed.unit),
(&placed.source, inputs.report.tier()),
)?;
Ok(PlacedFragment {
unit: placed.unit,
fragment: stated_projection(stated, key, reuse)?,
})
}
fn stated_projection(
stated: &StatedSource<'_>,
key: SourceKey,
reuse: &mut ProjectionStore,
) -> Result<Arc<SourceFragment>, GraphBuildError> {
match reuse.reused(&key, |held| source::states_current_claim(held, stated)) {
Some(retained) => Ok(retained),
None => Ok(reuse.retain(key, source::derived_projection(stated)?)),
}
}
fn plan_dependencies(
inputs: &Inputs<'_>,
bound: &BTreeMap<RustSnapshotUnitId, u32>,
) -> Result<Box<[DependencyProjection]>, GraphBuildError> {
inputs
.snapshot
.edges()
.iter()
.enumerate()
.map(|(index, edge)| {
let stated = (position(index), edge);
let source = validation::dependency_unit(bound.get(&edge.source()).copied(), stated)?;
let target = validation::dependency_unit(bound.get(&edge.target()).copied(), stated)?;
let (certainty, predicate) = mapping::activation(edge.activation());
Ok(DependencyProjection {
source,
target,
certainty,
evidence: DependencyEvidence::new(
Arc::from(edge.name()),
mapping::dependency_kind(edge.kind()),
predicate,
),
})
})
.collect()
}