use crate::error::GraphBuildError;
use crate::graph::CodeGraph;
use crate::id::{GraphNodeId, GraphReferenceId, position};
use crate::limits::GraphLimits;
use super::fragment::{FragmentSlot, ProjectionPlan, ReferenceProjection};
use super::index::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);
let units = assemble_containers(&mut state, plan)?;
assemble_sources(&mut state, plan)?;
assemble_definitions(&mut state, plan)?;
assemble_containment(&mut state, plan)?;
validation::check_containment_forest(&state, units)?;
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<usize, GraphBuildError> {
for unit in &plan.units {
let container = state.insert_node(unit.container_draft())?;
state.bind_container(container);
}
Ok(state.node_count())
}
fn assemble_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 = state.insert_node(unit.file_draft(path))?;
state.bind_file(reported, path, node)?;
state.contain(container, node);
}
}
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, unit, source.path())?;
let node = state.insert_node(projection.draft(file))?;
state.bind_definition(&projection.identity, node);
}
Ok(())
}
fn assemble_containment(
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 parent = match &projection.parent {
Some(owner) => validation::definition_node(state, owner, stated)?,
None => validation::unit_container(state, unit)?,
};
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(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, 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);
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))?;
}
}
Ok(())
}