use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_types::{Language, ResolutionGap, ResolutionTier, SourceSpan};
use crate::edge::{DependencyEvidence, EdgeDraft, GraphCertainty, GraphEdgeKind, GraphEdgeOrigin};
use crate::error::GraphBuildError;
use crate::id::{GraphNodeId, GraphReferenceId, position};
use crate::node::{GraphNodeKind, GraphNodeLocation, NodeDraft};
use crate::reference::{GraphReferenceKind, ReferenceDraft};
use super::identity::{DefinitionIdentity, SourceIdentity};
use super::index::ProjectionCapacity;
use super::validation;
pub(crate) struct UnitPlan {
pub(crate) key: Arc<str>,
pub(crate) language: Language,
pub(crate) name: Arc<str>,
pub(crate) kind: GraphNodeKind,
pub(crate) sources: Box<[Arc<str>]>,
}
impl UnitPlan {
pub(crate) fn container_draft(&self) -> NodeDraft {
NodeDraft {
language: self.language,
name: Arc::clone(&self.name),
kind: self.kind.clone(),
location: None,
}
}
pub(crate) fn file_draft(&self, path: &Arc<str>) -> NodeDraft {
NodeDraft {
language: self.language,
name: Arc::clone(path),
kind: GraphNodeKind::File,
location: Some(GraphNodeLocation::File {
path: Arc::clone(path),
}),
}
}
}
pub(crate) struct DefinitionProjection {
pub(crate) identity: Arc<DefinitionIdentity>,
pub(crate) language: Language,
pub(crate) kind: GraphNodeKind,
pub(crate) parent: Option<Arc<DefinitionIdentity>>,
}
impl DefinitionProjection {
pub(crate) fn draft(&self, file: GraphNodeId) -> NodeDraft {
NodeDraft {
language: self.language,
name: Arc::clone(self.identity.name()),
kind: self.kind.clone(),
location: Some(GraphNodeLocation::Span {
file,
span: self.identity.span().clone(),
}),
}
}
}
pub(crate) struct CandidateProjection {
pub(crate) target: Arc<DefinitionIdentity>,
pub(crate) kind: GraphEdgeKind,
pub(crate) certainty: GraphCertainty,
}
impl CandidateProjection {
pub(crate) fn draft(
&self,
site: (GraphNodeId, GraphReferenceId),
target: GraphNodeId,
) -> EdgeDraft {
let (source, reference) = site;
EdgeDraft {
source,
target,
kind: self.kind,
certainty: self.certainty,
origin: GraphEdgeOrigin::Reference { reference },
}
}
}
pub(crate) struct ReferenceProjection {
pub(crate) language: Language,
pub(crate) kind: GraphReferenceKind,
pub(crate) text: Arc<str>,
pub(crate) span: SourceSpan,
pub(crate) gaps: Box<[ResolutionGap]>,
pub(crate) enclosing: Option<Arc<DefinitionIdentity>>,
pub(crate) candidates: Box<[CandidateProjection]>,
}
impl ReferenceProjection {
pub(crate) fn draft(&self, source: GraphNodeId) -> ReferenceDraft {
ReferenceDraft {
source,
language: self.language,
kind: self.kind,
text: Arc::clone(&self.text),
span: self.span.clone(),
gaps: self.gaps.iter().copied().collect(),
}
}
}
pub(crate) struct DependencyProjection {
pub(crate) source: u32,
pub(crate) target: u32,
pub(crate) certainty: GraphCertainty,
pub(crate) evidence: DependencyEvidence,
}
impl DependencyProjection {
pub(crate) fn draft(&self, source: GraphNodeId, target: GraphNodeId) -> EdgeDraft {
EdgeDraft {
source,
target,
kind: GraphEdgeKind::DependsOn,
certainty: self.certainty,
origin: GraphEdgeOrigin::Dependency {
evidence: self.evidence.clone(),
},
}
}
}
pub(crate) struct SourceFragment {
pub(crate) source: SourceIdentity,
pub(crate) definitions: Box<[DefinitionProjection]>,
pub(crate) references: Box<[ReferenceProjection]>,
}
pub(crate) struct PlacedFragment {
pub(crate) unit: u32,
pub(crate) fragment: Arc<SourceFragment>,
}
pub(crate) struct PlacedSource {
pub(crate) unit: u32,
pub(crate) source: SourceIdentity,
}
#[derive(Clone, Copy)]
pub(crate) struct FragmentSlot {
pub(crate) fragment: u32,
pub(crate) slot: u32,
}
pub(crate) struct SourceSet {
units: Box<[UnitPlan]>,
located: Box<[BTreeMap<Arc<str>, u32>]>,
placed: Box<[PlacedSource]>,
}
impl SourceSet {
pub(crate) fn new(units: Box<[UnitPlan]>) -> Result<Self, GraphBuildError> {
let mut placed: Vec<PlacedSource> = Vec::new();
let located: Vec<BTreeMap<Arc<str>, u32>> = units
.iter()
.enumerate()
.map(|(index, unit)| unit_sources(&mut placed, position(index), unit))
.collect::<Result<_, GraphBuildError>>()?;
Ok(Self {
units,
located: located.into_boxed_slice(),
placed: placed.into_boxed_slice(),
})
}
pub(crate) fn units(&self) -> &[UnitPlan] {
&self.units
}
pub(crate) fn placed(&self) -> &[PlacedSource] {
&self.placed
}
pub(crate) fn locate(&self, unit: u32, path: &str) -> Option<(u32, &SourceIdentity)> {
let scope = self.located.get(crate::id::index_of(unit))?;
let source = *scope.get(path)?;
let placed = self.placed.get(crate::id::index_of(source))?;
Some((source, &placed.source))
}
pub(crate) fn finish(self) -> Box<[UnitPlan]> {
self.units
}
}
fn unit_sources(
placed: &mut Vec<PlacedSource>,
unit: u32,
plan: &UnitPlan,
) -> Result<BTreeMap<Arc<str>, u32>, GraphBuildError> {
let mut located = BTreeMap::new();
for path in &plan.sources {
let at = position(placed.len());
validation::distinct_source(located.insert(Arc::clone(path), at), unit, path)?;
placed.push(PlacedSource {
unit,
source: SourceIdentity::new(Arc::clone(&plan.key), Arc::clone(path)),
});
}
Ok(located)
}
pub(crate) struct ProjectionPlan {
pub(crate) tier: ResolutionTier,
pub(crate) units: Box<[UnitPlan]>,
pub(crate) fragments: Box<[PlacedFragment]>,
pub(crate) definitions: Box<[FragmentSlot]>,
pub(crate) references: Box<[FragmentSlot]>,
pub(crate) dependencies: Box<[DependencyProjection]>,
pub(crate) capacity: ProjectionCapacity,
}
impl ProjectionPlan {
pub(crate) fn definition(&self, at: FragmentSlot) -> Option<&DefinitionProjection> {
self.fragment(at.fragment)?
.definitions
.get(crate::id::index_of(at.slot))
}
pub(crate) fn reference(&self, at: FragmentSlot) -> Option<&ReferenceProjection> {
self.fragment(at.fragment)?
.references
.get(crate::id::index_of(at.slot))
}
pub(crate) fn placed(&self, fragment: u32) -> Option<&PlacedFragment> {
self.fragments.get(crate::id::index_of(fragment))
}
pub(crate) fn source(&self, fragment: u32) -> Option<&SourceIdentity> {
self.fragment(fragment).map(|held| &held.source)
}
fn fragment(&self, fragment: u32) -> Option<&SourceFragment> {
self.placed(fragment).map(|placed| placed.fragment.as_ref())
}
}