use std::sync::Arc;
use pedant_core::resolution::rust::{
RustResolutionSnapshot, RustResolutionUnit, RustSnapshotEdge, RustSnapshotUnitId,
RustTargetResolution, RustUnitBinding,
};
use pedant_types::{ResolutionRecord, ResolutionReport, ResolutionUnit, SymbolReference};
use crate::containment::ContainmentEdge;
use crate::error::GraphBuildError;
use crate::id::{GraphNodeId, index_of, position};
use super::fragment::{
DefinitionProjection, FragmentSlot, ProjectionPlan, ReferenceProjection, SourceSet, UnitPlan,
};
use super::identity::{DefinitionIdentity, DefinitionTable, SourceIdentity};
use super::index::{ProjectionState, SourceScope};
#[derive(Clone, Copy, PartialEq, Eq)]
enum Visit {
Pending,
Active,
Done,
}
pub(crate) fn check_root_target(
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
) -> Result<(), GraphBuildError> {
match snapshot.root_target() == resolution.root_target() {
true => Ok(()),
false => Err(GraphBuildError::RootTargetMismatch),
}
}
pub(crate) fn check_snapshot_identity(
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
) -> Result<(), GraphBuildError> {
match snapshot.fingerprint() == resolution.snapshot_fingerprint() {
true => Ok(()),
false => Err(GraphBuildError::SnapshotFingerprintMismatch),
}
}
pub(crate) fn stated_binding(
resolution: &RustTargetResolution,
unit: &ResolutionUnit,
) -> Result<RustSnapshotUnitId, GraphBuildError> {
resolution
.unit(unit.id())
.map(RustUnitBinding::snapshot_unit)
.ok_or(GraphBuildError::MissingUnitBinding {
unit: unit.id().index(),
})
}
pub(crate) fn snapshot_instance(
snapshot: &RustResolutionSnapshot,
unit: (RustSnapshotUnitId, u32),
) -> Result<&RustResolutionUnit, GraphBuildError> {
let (snapshot_unit, reported) = unit;
snapshot
.unit(snapshot_unit)
.ok_or(GraphBuildError::DanglingUnitBinding { unit: reported })
}
pub(crate) fn distinct_binding(held: Option<u32>, unit: u32) -> Result<(), GraphBuildError> {
match held {
None => Ok(()),
Some(held) => Err(GraphBuildError::SharedUnitBinding { held, unit }),
}
}
pub(crate) fn distinct_source(
held: Option<u32>,
unit: u32,
path: &str,
) -> Result<(), GraphBuildError> {
match held {
None => Ok(()),
Some(_) => Err(GraphBuildError::RepeatedUnitSource {
unit,
path: Box::from(path),
}),
}
}
pub(crate) fn planned_unit(units: &[UnitPlan], unit: u32) -> Result<&UnitPlan, GraphBuildError> {
units
.get(index_of(unit))
.ok_or(GraphBuildError::MissingUnitBinding { unit })
}
pub(crate) fn source_digest(
snapshot: &RustResolutionSnapshot,
unit: u32,
path: &str,
) -> Result<[u8; 32], GraphBuildError> {
snapshot
.source(path)
.map(|source| *source.digest())
.ok_or_else(|| GraphBuildError::MissingSourceNode {
unit,
path: Box::from(path),
})
}
pub(crate) fn instantiated_source<'a>(
sources: &'a SourceSet,
unit: u32,
path: &str,
) -> Result<(u32, &'a SourceIdentity), GraphBuildError> {
sources
.locate(unit, path)
.ok_or_else(|| GraphBuildError::MissingSourceNode {
unit,
path: Box::from(path),
})
}
pub(crate) fn definition_identity(
table: &DefinitionTable,
definition: u32,
) -> Result<&Arc<DefinitionIdentity>, GraphBuildError> {
table
.identity(definition)
.ok_or(GraphBuildError::MissingDefinitionNode { definition })
}
pub(crate) fn definition_fragment(
table: &DefinitionTable,
definition: u32,
) -> Result<u32, GraphBuildError> {
table
.fragment(definition)
.ok_or(GraphBuildError::MissingDefinitionNode { definition })
}
pub(crate) fn placed_slot(
slot: Option<FragmentSlot>,
unit: u32,
path: &str,
) -> Result<FragmentSlot, GraphBuildError> {
slot.ok_or_else(|| GraphBuildError::MissingSourceNode {
unit,
path: Box::from(path),
})
}
pub(crate) fn dependency_unit(
bound: Option<u32>,
stated: (u32, &RustSnapshotEdge),
) -> Result<u32, GraphBuildError> {
let (edge, declaration) = stated;
bound.ok_or_else(|| GraphBuildError::MissingDependencyUnit {
edge,
alias: Box::from(declaration.name()),
})
}
pub(crate) fn resolved_references(
report: &ResolutionReport,
) -> Result<Box<[(&SymbolReference, &ResolutionRecord)]>, GraphBuildError> {
let references = report.references();
let records = report.resolutions();
match references.len() == records.len() {
true => Ok(references.iter().zip(records).collect()),
false => Err(GraphBuildError::ReferenceRecordMismatch {
references: position(references.len()),
records: position(records.len()),
}),
}
}
pub(crate) fn planned_definition(
plan: &ProjectionPlan,
at: FragmentSlot,
definition: u32,
) -> Result<&DefinitionProjection, GraphBuildError> {
plan.definition(at)
.ok_or(GraphBuildError::MissingDefinitionNode { definition })
}
pub(crate) fn planned_reference(
plan: &ProjectionPlan,
at: FragmentSlot,
reference: u32,
) -> Result<&ReferenceProjection, GraphBuildError> {
plan.reference(at)
.ok_or(GraphBuildError::MissingReferenceRecord { reference })
}
pub(crate) fn fragment_source(
plan: &ProjectionPlan,
fragment: u32,
definition: u32,
) -> Result<&SourceIdentity, GraphBuildError> {
plan.source(fragment)
.ok_or(GraphBuildError::MissingDefinitionNode { definition })
}
pub(crate) fn fragment_unit(plan: &ProjectionPlan, fragment: u32) -> Result<u32, GraphBuildError> {
plan.placed(fragment)
.map(|placed| placed.unit)
.ok_or(GraphBuildError::MissingUnitBinding { unit: fragment })
}
pub(crate) fn unit_scope(
held: Option<&mut SourceScope>,
unit: u32,
) -> Result<&mut SourceScope, GraphBuildError> {
held.ok_or(GraphBuildError::MissingUnitBinding { unit })
}
pub(crate) fn unit_container(
state: &ProjectionState,
unit: u32,
) -> Result<GraphNodeId, GraphBuildError> {
state
.container(unit)
.ok_or(GraphBuildError::MissingUnitBinding { unit })
}
pub(crate) fn source_node(
state: &ProjectionState,
unit: u32,
path: &str,
) -> Result<GraphNodeId, GraphBuildError> {
state
.file(unit, path)
.ok_or_else(|| GraphBuildError::MissingSourceNode {
unit,
path: Box::from(path),
})
}
pub(crate) fn definition_node(
state: &ProjectionState,
identity: &DefinitionIdentity,
stated: u32,
) -> Result<GraphNodeId, GraphBuildError> {
state
.definition(identity)
.ok_or(GraphBuildError::MissingDefinitionNode { definition: stated })
}
pub(crate) fn check_containment_forest(
state: &ProjectionState,
units: usize,
) -> Result<(), GraphBuildError> {
let parents = stated_parents(state)?;
check_every_node_is_parented(&parents, units)?;
check_no_unit_root_is_contained(&parents, units)?;
check_acyclic(&parents)
}
fn stated_parents(state: &ProjectionState) -> Result<Box<[Option<GraphNodeId>]>, GraphBuildError> {
let mut parents: Vec<Option<GraphNodeId>> = vec![None; state.node_count()];
for edge in state.containment() {
let slot = parent_slot(&mut parents, edge)?;
match slot {
Some(existing) => {
return Err(GraphBuildError::MultiplyContained {
parent: existing.index(),
child: edge.child().index(),
});
}
None => *slot = Some(edge.parent()),
}
}
Ok(parents.into_boxed_slice())
}
fn parent_slot<'a>(
parents: &'a mut [Option<GraphNodeId>],
edge: &ContainmentEdge,
) -> Result<&'a mut Option<GraphNodeId>, GraphBuildError> {
let child = edge.child();
parents
.get_mut(index_of(child.index()))
.ok_or(GraphBuildError::UnknownContainmentNode {
node: child.index(),
})
}
fn check_every_node_is_parented(
parents: &[Option<GraphNodeId>],
units: usize,
) -> Result<(), GraphBuildError> {
parents
.iter()
.enumerate()
.skip(units)
.find(|(_, parent)| parent.is_none())
.map_or(Ok(()), |(index, _)| {
Err(GraphBuildError::UnparentedNode {
node: position(index),
})
})
}
fn check_no_unit_root_is_contained(
parents: &[Option<GraphNodeId>],
units: usize,
) -> Result<(), GraphBuildError> {
parents
.iter()
.enumerate()
.take(units)
.find_map(|(index, parent)| parent.map(|held| (index, held)))
.map_or(Ok(()), |(index, parent)| {
Err(GraphBuildError::RootHasParent {
root: position(index),
parent: parent.index(),
})
})
}
fn check_acyclic(parents: &[Option<GraphNodeId>]) -> Result<(), GraphBuildError> {
let mut visits = vec![Visit::Pending; parents.len()];
let mut chain: Vec<usize> = Vec::new();
for start in 0..parents.len() {
walk_ancestors(parents, &mut visits, &mut chain, start)?;
}
Ok(())
}
fn walk_ancestors(
parents: &[Option<GraphNodeId>],
visits: &mut [Visit],
chain: &mut Vec<usize>,
start: usize,
) -> Result<(), GraphBuildError> {
match visit_of(visits, start)? {
Visit::Pending => (),
Visit::Active | Visit::Done => return Ok(()),
}
chain.clear();
let mut current = Some(start);
let mut cycle = None;
while let Some(index) = current {
let visit = visit_of(visits, index)?;
cycle = (visit == Visit::Active).then_some(index);
match visit {
Visit::Pending => (),
Visit::Active | Visit::Done => break,
}
mark(visits, index, Visit::Active)?;
chain.push(index);
current = parents
.get(index)
.and_then(|parent| *parent)
.map(|parent| index_of(parent.index()));
}
for index in chain.drain(..) {
mark(visits, index, Visit::Done)?;
}
match cycle {
Some(index) => Err(GraphBuildError::ContainmentCycle {
node: position(index),
}),
None => Ok(()),
}
}
fn visit_of(visits: &[Visit], index: usize) -> Result<Visit, GraphBuildError> {
visits
.get(index)
.copied()
.ok_or(GraphBuildError::UnknownContainmentNode {
node: position(index),
})
}
fn mark(visits: &mut [Visit], index: usize, visit: Visit) -> Result<(), GraphBuildError> {
match visits.get_mut(index) {
Some(slot) => {
*slot = visit;
Ok(())
}
None => Err(GraphBuildError::UnknownContainmentNode {
node: position(index),
}),
}
}