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::limits::GraphLimits;
use crate::projection::assembly;
use crate::projection::draft::{
DependencyProjection, PlacedFragment, ProjectionPlan, SourceFragment, StatedContainer,
StatedDependency, UnitPlan,
};
use crate::projection::placement::{DefinitionTable, RecordPlacement, SourceSet};
use crate::projection::state::ProjectionCapacity;
use crate::projection::validation as neutral;
use super::claim::SourceKey;
use super::mapping::{self, Vocabulary};
use super::reuse::ProjectionStore;
use super::source::{self, 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>,
}
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: neutral::resolved_references(report)?,
};
let planned = plan_units(&inputs, resolution)?;
let capacity = ProjectionCapacity::stated(
planned.units.len(),
report,
&inputs.resolved,
snapshot.edges().len(),
);
let sources = SourceSet::new(planned.units.into_boxed_slice())?;
let table = DefinitionTable::new(report, &sources)?;
let placement = RecordPlacement::stated(report, &inputs.resolved, (&sources, &table))?;
let fragments = plan_fragments(&inputs, (&sources, &table, &placement), reuse)?;
let dependencies = plan_dependencies(&inputs, &planned.bound)?;
Ok(ProjectionPlan {
tier: report.tier(),
units: sources.finish(),
declarations: Box::default(),
fragments,
definitions: placement.definitions,
references: placement.references,
dependencies,
capacity,
})
}
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 = neutral::bound_instance(inputs.snapshot.unit(snapshot_unit), reported)?;
neutral::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(),
container: Some(StatedContainer {
name: Arc::from(unit.name()),
kind: inputs.vocabulary.unit_root(instance.kind()),
}),
parent: None,
sources: instance.sources().into(),
});
}
Ok(planned)
}
fn plan_fragments<'a>(
inputs: &Inputs<'a>,
placed: (&SourceSet, &DefinitionTable, &RecordPlacement<'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> {
DependencyProjection::stated(inputs.snapshot.edges(), |edge| {
let (certainty, predicate) = mapping::activation(edge.activation());
StatedDependency {
bound: (
bound.get(&edge.source()).copied(),
bound.get(&edge.target()).copied(),
),
certainty,
evidence: DependencyEvidence::new(
Arc::from(edge.name()),
mapping::dependency_kind(edge.kind()),
predicate,
),
}
})
}