use crate::edge::GraphEdgeKind;
use crate::error::GraphBuildError;
use crate::graph::CodeGraph;
use crate::id::{GraphNodeId, GraphReferenceId, position};
use crate::limits::{GraphCollection, GraphLimits};
use super::draft::{FragmentSlot, ProjectionPlan, ReferenceProjection};
use super::forest;
use super::state::ProjectionState;
use super::validation;
struct AssembledReference<'plan> {
id: GraphReferenceId,
source: GraphNodeId,
projection: &'plan ReferenceProjection,
}
pub(crate) fn assemble(
plan: &ProjectionPlan,
limits: GraphLimits,
) -> Result<CodeGraph, GraphBuildError> {
let mut state = ProjectionState::new(limits, plan.capacity);
assemble_containers(&mut state, plan)?;
assemble_sources(&mut state, plan)?;
assemble_definitions(&mut state, plan)?;
assemble_declared_containers(&mut state, plan)?;
let roots = assemble_containment(&mut state, plan)?;
forest::check_containment_forest(&state, &roots)?;
let references = assemble_references(&mut state, plan)?;
assemble_dependencies(&mut state, plan)?;
assemble_candidates(&mut state, &references)?;
Ok(state.finish(plan.tier))
}
fn assemble_containers(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for (index, unit) in plan.units.iter().enumerate() {
let Some(draft) = unit.container_draft() else {
continue;
};
let container = state.insert_node(draft)?;
state.bind_container(position(index), container)?;
}
Ok(())
}
fn assemble_sources(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
let mut placed = Vec::with_capacity(plan.fragments.len());
for (index, unit) in plan.units.iter().enumerate() {
let reported = position(index);
for path in &unit.sources {
let node = state.insert_node(unit.file_draft(path))?;
state.bind_file(reported, path, node)?;
placed.push(node);
}
}
state.bind_sources(placed.into_boxed_slice());
Ok(())
}
fn assemble_definitions(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for (index, at) in plan.definitions.iter().enumerate() {
let stated = position(index);
let projection = validation::planned_definition(plan, *at, stated)?;
let unit = validation::fragment_unit(plan, at.fragment)?;
let source = validation::fragment_source(plan, at.fragment, stated)?;
let file = validation::source_node(state.fragment_file(at.fragment), unit, source.path())?;
let node = state.insert_node(projection.draft(file))?;
state.bind_definition(&projection.identity, node);
}
Ok(())
}
fn assemble_declared_containers(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for declared in &plan.declarations {
let definition = declared.definition;
let at = validation::stated_definition(plan, definition)?;
let projection = validation::planned_definition(plan, at, definition)?;
let node = validation::definition_node(state, &projection.identity, definition)?;
state.bind_container(declared.unit, node)?;
}
Ok(())
}
fn assemble_containment(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<Box<[GraphNodeId]>, GraphBuildError> {
let roots = contain_units(state, plan)?;
contain_sources(state, plan)?;
contain_definitions(state, plan)?;
Ok(roots)
}
fn contain_units(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<Box<[GraphNodeId]>, GraphBuildError> {
let mut roots = Vec::with_capacity(plan.units.len());
for (index, unit) in plan.units.iter().enumerate() {
let child = validation::unit_container(state, position(index))?;
match unit.parent {
None => roots.push(child),
Some(owner) => {
let parent = validation::unit_container(state, owner)?;
state.contain(parent, child);
}
}
}
Ok(roots.into_boxed_slice())
}
fn contain_sources(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for (index, unit) in plan.units.iter().enumerate() {
let reported = position(index);
let container = validation::unit_container(state, reported)?;
for path in &unit.sources {
let node = validation::source_node(state.file(reported, path), reported, path)?;
state.contain(container, node);
}
}
Ok(())
}
fn contain_definitions(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for (index, at) in plan.definitions.iter().enumerate() {
let stated = position(index);
let projection = validation::planned_definition(plan, *at, stated)?;
let unit = validation::fragment_unit(plan, at.fragment)?;
let child = validation::definition_node(state, &projection.identity, stated)?;
let container = validation::unit_container(state, unit)?;
let parent = match (child == container, &projection.parent) {
(true, _) => continue,
(false, Some(owner)) => validation::definition_node(state, owner, stated)?,
(false, None) => container,
};
state.contain(parent, child);
}
Ok(())
}
fn assemble_references<'plan>(
state: &mut ProjectionState,
plan: &'plan ProjectionPlan,
) -> Result<Box<[AssembledReference<'plan>]>, GraphBuildError> {
let mut assembled =
Vec::with_capacity(state.reserved(GraphCollection::Reference, plan.references.len()));
for (index, at) in plan.references.iter().enumerate() {
let stated = position(index);
let projection = validation::planned_reference(plan, *at, stated)?;
let source = reference_source(state, (plan, projection), (*at, stated))?;
let id = state.insert_reference(projection.draft(source))?;
assembled.push(AssembledReference {
id,
source,
projection,
});
}
Ok(assembled.into_boxed_slice())
}
fn reference_source(
state: &ProjectionState,
planned: (&ProjectionPlan, &ReferenceProjection),
stated: (FragmentSlot, u32),
) -> Result<GraphNodeId, GraphBuildError> {
let (plan, projection) = planned;
let (at, reference) = stated;
let unit = validation::fragment_unit(plan, at.fragment)?;
match &projection.enclosing {
Some(enclosing) => validation::definition_node(state, enclosing, reference),
None => validation::source_node(
state.fragment_file(at.fragment),
unit,
projection.span.file(),
),
}
}
fn assemble_dependencies(
state: &mut ProjectionState,
plan: &ProjectionPlan,
) -> Result<(), GraphBuildError> {
for dependency in &plan.dependencies {
let source = validation::unit_container(state, dependency.source)?;
let target = validation::unit_container(state, dependency.target)?;
state.insert_edge(dependency.draft(source, target))?;
}
Ok(())
}
fn assemble_candidates(
state: &mut ProjectionState,
references: &[AssembledReference<'_>],
) -> Result<(), GraphBuildError> {
for (index, assembled) in references.iter().enumerate() {
let stated = position(index);
let kind = GraphEdgeKind::of_reference(assembled.projection.kind);
for candidate in &assembled.projection.candidates {
let target = validation::definition_node(state, &candidate.target, stated)?;
state.insert_edge(candidate.draft((assembled.source, assembled.id), target, kind))?;
}
}
Ok(())
}