use std::sync::Arc;
use pedant_types::{
Language, ReferenceKind, ResolutionGap, ResolutionRecord, ResolutionTier, SourceSpan,
SymbolDefinition, SymbolReference,
};
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::placement::{
DefinitionIdentity, DefinitionTable, PlacedSource, SourceIdentity, SourceRecords,
};
use super::state::ProjectionCapacity;
use super::validation;
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct StatedContainer {
pub(crate) name: Arc<str>,
pub(crate) kind: GraphNodeKind,
}
pub(crate) struct UnitDeclaration {
pub(crate) unit: u32,
pub(crate) definition: u32,
}
pub(crate) struct UnitPlan {
pub(crate) key: Arc<str>,
pub(crate) language: Language,
pub(crate) container: Option<StatedContainer>,
pub(crate) parent: Option<u32>,
pub(crate) sources: Box<[Arc<str>]>,
}
impl UnitPlan {
pub(crate) fn container_draft(&self) -> Option<NodeDraft> {
self.container.as_ref().map(|stated| NodeDraft {
language: self.language,
name: Arc::clone(&stated.name),
kind: stated.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 stated<Named>(
table: &DefinitionTable,
reported: (u32, &SymbolDefinition),
named: Named,
) -> Result<Self, GraphBuildError>
where
Named: Fn(&SymbolDefinition) -> Result<GraphNodeKind, GraphBuildError>,
{
let (at, definition) = reported;
Ok(Self {
identity: Arc::clone(validation::definition_identity(table, at)?),
language: definition.language(),
kind: named(definition)?,
parent: validation::optional_identity(
table,
definition.parent().map(|parent| parent.index()),
)?,
})
}
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) certainty: GraphCertainty,
}
impl CandidateProjection {
pub(crate) fn stated(
table: &DefinitionTable,
record: &ResolutionRecord,
) -> Result<Box<[Self]>, GraphBuildError> {
record
.candidates()
.iter()
.map(|candidate| {
let target =
validation::definition_identity(table, candidate.definition().index())?;
Ok(Self {
target: Arc::clone(target),
certainty: GraphCertainty::of(candidate.certainty()),
})
})
.collect()
}
pub(crate) fn draft(
&self,
site: (GraphNodeId, GraphReferenceId),
target: GraphNodeId,
kind: GraphEdgeKind,
) -> EdgeDraft {
let (source, reference) = site;
EdgeDraft {
source,
target,
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: Arc<[ResolutionGap]>,
pub(crate) enclosing: Option<Arc<DefinitionIdentity>>,
pub(crate) candidates: Box<[CandidateProjection]>,
}
impl ReferenceProjection {
pub(crate) fn stated(
table: &DefinitionTable,
reported: (&SymbolReference, &ResolutionRecord),
named: Option<GraphReferenceKind>,
) -> Result<Self, GraphBuildError> {
let (reference, record) = reported;
Ok(Self {
language: reference.language(),
kind: validation::stated_reference_kind(named, reference)?,
text: Arc::from(reference.text()),
span: SymbolReference::span(reference).clone(),
gaps: record.gaps().into(),
enclosing: validation::optional_identity(
table,
reference
.enclosing_definition()
.map(|enclosing| enclosing.index()),
)?,
candidates: CandidateProjection::stated(table, record)?,
})
}
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: Arc::clone(&self.gaps),
}
}
}
pub(crate) struct StatedDependency {
pub(crate) bound: (Option<u32>, Option<u32>),
pub(crate) certainty: GraphCertainty,
pub(crate) evidence: DependencyEvidence,
}
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 stated<Edge, Declared>(
edges: &[Edge],
declared: Declared,
) -> Result<Box<[Self]>, GraphBuildError>
where
Declared: Fn(&Edge) -> StatedDependency,
{
edges
.iter()
.enumerate()
.map(|(index, edge)| Self::bound(position(index), declared(edge)))
.collect()
}
fn bound(edge: u32, declared: StatedDependency) -> Result<Self, GraphBuildError> {
let (depending, depended) = declared.bound;
let source = validation::dependency_unit(depending, (edge, declared.evidence.alias()))?;
let target = validation::dependency_unit(depended, (edge, declared.evidence.alias()))?;
Ok(Self {
source,
target,
certainty: declared.certainty,
evidence: declared.evidence,
})
}
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]>,
}
impl SourceFragment {
pub(crate) fn derived<Named>(
placed: (&PlacedSource, &DefinitionTable),
records: &SourceRecords<'_>,
named: (Named, fn(ReferenceKind) -> Option<GraphReferenceKind>),
) -> Result<Self, GraphBuildError>
where
Named: Fn(&SymbolDefinition) -> Result<GraphNodeKind, GraphBuildError>,
{
let (source, table) = placed;
let (definition_kind, reference_kind) = named;
Ok(Self {
source: source.source.shared(),
definitions: records
.definitions
.iter()
.copied()
.map(|reported| DefinitionProjection::stated(table, reported, &definition_kind))
.collect::<Result<_, GraphBuildError>>()?,
references: records
.references
.iter()
.copied()
.map(|reported| stated_reference(table, reported, reference_kind))
.collect::<Result<_, GraphBuildError>>()?,
})
}
}
fn stated_reference(
table: &DefinitionTable,
reported: (&SymbolReference, &ResolutionRecord),
named: fn(ReferenceKind) -> Option<GraphReferenceKind>,
) -> Result<ReferenceProjection, GraphBuildError> {
let (reference, _) = reported;
ReferenceProjection::stated(table, reported, named(reference.kind()))
}
pub(crate) struct PlacedFragment {
pub(crate) unit: u32,
pub(crate) fragment: Arc<SourceFragment>,
}
#[derive(Clone, Copy)]
pub(crate) struct FragmentSlot {
pub(crate) fragment: u32,
pub(crate) slot: u32,
}
pub(crate) struct ProjectionPlan {
pub(crate) tier: ResolutionTier,
pub(crate) units: Box<[UnitPlan]>,
pub(crate) declarations: Box<[UnitDeclaration]>,
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())
}
}