use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_core::resolution::go::{
GoProjectResolution, GoResolutionSnapshot, GoSnapshotModuleId, GoSnapshotUnitId, GoUnitBinding,
};
use pedant_types::{Language, ResolutionReport, ResolutionUnit, SymbolDefinition, SymbolKind};
use crate::edge::{DependencyEvidence, GraphCertainty, GraphDependencyKind};
use crate::error::GraphBuildError;
use crate::id::{index_of, position};
use crate::projection::draft::{
DependencyProjection, StatedContainer, StatedDependency, UnitDeclaration, UnitPlan,
};
use crate::projection::validation as neutral;
use super::mapping::Vocabulary;
use super::validation;
pub(crate) struct PlannedUnits {
pub(crate) units: Box<[UnitPlan]>,
pub(crate) declarations: Box<[UnitDeclaration]>,
pub(crate) modules: BTreeMap<GoSnapshotModuleId, u32>,
}
pub(crate) fn plan_units(
snapshot: &GoResolutionSnapshot,
resolution: &GoProjectResolution,
vocabulary: &Vocabulary,
) -> Result<PlannedUnits, GraphBuildError> {
let report = resolution.report();
let declared = declared_packages(report)?;
let mut planned = PlannedContexts::opening(report);
for unit in report.units() {
plan_context(&mut planned, (snapshot, resolution), (unit, &declared))?;
}
let modules = module_positions(snapshot, planned.units.len());
planned.root(&modules)?;
Ok(planned.finish((snapshot, vocabulary), modules))
}
pub(crate) fn plan_dependencies(
snapshot: &GoResolutionSnapshot,
modules: &BTreeMap<GoSnapshotModuleId, u32>,
) -> Result<Box<[DependencyProjection]>, GraphBuildError> {
DependencyProjection::stated(snapshot.edges(), |edge| StatedDependency {
bound: (
modules.get(&edge.source()).copied(),
modules.get(&edge.target()).copied(),
),
certainty: GraphCertainty::Resolved,
evidence: DependencyEvidence::new(
Arc::from(edge.module_path()),
GraphDependencyKind::Normal,
None,
),
})
}
struct PlannedContexts {
units: Vec<UnitPlan>,
declarations: Vec<UnitDeclaration>,
rooted: Vec<(u32, GoSnapshotModuleId)>,
bound: BTreeMap<GoSnapshotUnitId, u32>,
}
impl PlannedContexts {
fn opening(report: &ResolutionReport) -> Self {
let stated = report.units().len();
Self {
units: Vec::with_capacity(stated),
declarations: Vec::with_capacity(stated),
rooted: Vec::with_capacity(stated),
bound: BTreeMap::new(),
}
}
fn root(&mut self, modules: &BTreeMap<GoSnapshotModuleId, u32>) -> Result<(), GraphBuildError> {
for (plan, (reported, module)) in self.units.iter_mut().zip(&self.rooted) {
plan.parent = Some(neutral::bound_instance(
modules.get(module).copied(),
*reported,
)?);
}
Ok(())
}
fn finish(
self,
planned: (&GoResolutionSnapshot, &Vocabulary),
modules: BTreeMap<GoSnapshotModuleId, u32>,
) -> PlannedUnits {
let (snapshot, vocabulary) = planned;
let mut units = self.units;
units.extend(planned_modules(snapshot, vocabulary));
PlannedUnits {
units: units.into_boxed_slice(),
declarations: self.declarations.into_boxed_slice(),
modules,
}
}
}
fn plan_context(
planned: &mut PlannedContexts,
supplied: (&GoResolutionSnapshot, &GoProjectResolution),
stated: (&ResolutionUnit, &[Option<u32>]),
) -> Result<(), GraphBuildError> {
let (snapshot, resolution) = supplied;
let (unit, declared) = stated;
let reported = unit.id().index();
let binding = validation::stated_binding(resolution, unit)?;
let snapshot_unit = binding.snapshot_unit();
let instance = neutral::bound_instance(snapshot.unit(snapshot_unit), reported)?;
neutral::distinct_binding(planned.bound.get(&snapshot_unit).copied(), reported)?;
planned.bound.insert(snapshot_unit, reported);
let at = position(planned.units.len());
planned.declarations.push(UnitDeclaration {
unit: at,
definition: validation::package_declaration(
declared.get(index_of(reported)).copied().flatten(),
reported,
)?,
});
planned
.rooted
.push((reported, GoUnitBinding::module(binding)));
planned.units.push(UnitPlan {
key: Arc::from(unit.key()),
language: unit.language(),
container: None,
parent: None,
sources: instance.sources().into(),
});
Ok(())
}
fn module_positions(
snapshot: &GoResolutionSnapshot,
contexts: usize,
) -> BTreeMap<GoSnapshotModuleId, u32> {
snapshot
.modules()
.iter()
.enumerate()
.map(|(index, module)| (module.id(), position(contexts.saturating_add(index))))
.collect()
}
fn planned_modules<'a>(
snapshot: &'a GoResolutionSnapshot,
vocabulary: &'a Vocabulary,
) -> impl Iterator<Item = UnitPlan> + 'a {
snapshot.modules().iter().map(|module| {
let path: Arc<str> = Arc::from(module.path());
UnitPlan {
key: Arc::clone(&path),
language: Language::Go,
container: Some(StatedContainer {
name: path,
kind: vocabulary.module_root(),
}),
parent: None,
sources: Box::default(),
}
})
}
fn declared_packages(report: &ResolutionReport) -> Result<Box<[Option<u32>]>, GraphBuildError> {
let mut declared: Box<[Option<u32>]> = vec![None; report.units().len()].into_boxed_slice();
for (index, definition) in report.definitions().iter().enumerate() {
let reported = definition.unit().index();
match definition.kind() {
SymbolKind::Package => declare_package(&mut declared, reported, position(index))?,
SymbolKind::Module
| SymbolKind::Function
| SymbolKind::Method
| SymbolKind::Struct
| SymbolKind::Enum
| SymbolKind::Union
| SymbolKind::Trait
| SymbolKind::Interface
| SymbolKind::DefinedType
| SymbolKind::TypeAlias
| SymbolKind::Constant
| SymbolKind::Variable
| SymbolKind::Static
| SymbolKind::Field => continue,
}
}
Ok(declared)
}
fn declare_package(
declared: &mut [Option<u32>],
unit: u32,
definition: u32,
) -> Result<(), GraphBuildError> {
let slot = neutral::unit_slot(declared.get_mut(index_of(unit)), unit)?;
validation::distinct_declaration(*slot, unit)?;
*slot = Some(definition);
Ok(())
}
pub(crate) fn holder_kind(
report: &ResolutionReport,
definition: &SymbolDefinition,
) -> Result<Option<SymbolKind>, GraphBuildError> {
match definition.parent() {
None => Ok(None),
Some(holder) => validation::holder_kind(report, holder.index()).map(Some),
}
}