use std::collections::{BTreeMap, HashMap};
use std::hash::{BuildHasherDefault, DefaultHasher};
use std::sync::Arc;
use pedant_types::{ResolutionRecord, ResolutionReport, ResolutionTier, SymbolReference};
use crate::containment::ContainmentEdge;
use crate::edge::EdgeDraft;
use crate::error::GraphBuildError;
use crate::graph::{CodeGraph, GraphRecords, RecordCapacity, reserved};
use crate::id::{GraphEdgeId, GraphNodeId, GraphReferenceId, index_of};
use crate::limits::{GraphCollection, GraphLimits};
use crate::node::NodeDraft;
use crate::reference::ReferenceDraft;
use super::placement::DefinitionIdentity;
use super::validation;
pub(crate) type SourceScope = BTreeMap<Arc<str>, GraphNodeId>;
type DefinitionScope =
HashMap<Arc<DefinitionIdentity>, GraphNodeId, BuildHasherDefault<DefaultHasher>>;
#[derive(Clone, Copy)]
pub(crate) struct ProjectionCapacity {
pub(crate) units: usize,
pub(crate) definitions: usize,
pub(crate) references: usize,
pub(crate) edges: usize,
}
impl ProjectionCapacity {
pub(crate) fn stated(
units: usize,
report: &ResolutionReport,
resolved: &[(&SymbolReference, &ResolutionRecord)],
dependencies: usize,
) -> Self {
Self {
units,
definitions: report.definitions().len(),
references: resolved.len(),
edges: resolved.iter().fold(dependencies, |total, (_, record)| {
total.saturating_add(record.candidates().len())
}),
}
}
fn records(&self) -> RecordCapacity {
RecordCapacity {
nodes: self.units.saturating_add(self.definitions),
references: self.references,
edges: self.edges,
}
}
}
pub(crate) struct ProjectionState {
records: GraphRecords,
containers: Box<[Option<GraphNodeId>]>,
files: Box<[SourceScope]>,
sources: Box<[GraphNodeId]>,
definitions: DefinitionScope,
}
impl ProjectionState {
pub(crate) fn new(limits: GraphLimits, capacity: ProjectionCapacity) -> Self {
let units = capacity.units;
let definitions = reserved(capacity.definitions, limits.ceiling(GraphCollection::Node));
Self {
records: GraphRecords::new(limits, capacity.records()),
containers: vec![None; units].into_boxed_slice(),
files: (0..units).map(|_| SourceScope::new()).collect(),
sources: Box::default(),
definitions: DefinitionScope::with_capacity_and_hasher(
definitions,
BuildHasherDefault::default(),
),
}
}
pub(crate) fn reserved(&self, collection: GraphCollection, stated: usize) -> usize {
self.records.reserved(collection, stated)
}
pub(crate) fn insert_node(&mut self, draft: NodeDraft) -> Result<GraphNodeId, GraphBuildError> {
self.records.insert_node(draft)
}
pub(crate) fn insert_reference(
&mut self,
draft: ReferenceDraft,
) -> Result<GraphReferenceId, GraphBuildError> {
self.records.insert_reference(draft)
}
pub(crate) fn insert_edge(&mut self, draft: EdgeDraft) -> Result<GraphEdgeId, GraphBuildError> {
self.records.insert_edge(draft)
}
pub(crate) fn contain(&mut self, parent: GraphNodeId, child: GraphNodeId) {
self.records.contain(parent, child);
}
pub(crate) fn bind_container(
&mut self,
unit: u32,
container: GraphNodeId,
) -> Result<(), GraphBuildError> {
let slot = validation::unit_slot(self.containers.get_mut(index_of(unit)), unit)?;
validation::unbound_container(*slot, unit)?;
*slot = Some(container);
Ok(())
}
pub(crate) fn container(&self, unit: u32) -> Option<GraphNodeId> {
self.containers.get(index_of(unit)).copied().flatten()
}
pub(crate) fn bind_file(
&mut self,
unit: u32,
path: &Arc<str>,
node: GraphNodeId,
) -> Result<(), GraphBuildError> {
let scope = validation::unit_scope(self.files.get_mut(index_of(unit)), unit)?;
scope.insert(Arc::clone(path), node);
Ok(())
}
pub(crate) fn file(&self, unit: u32, path: &str) -> Option<GraphNodeId> {
self.files
.get(index_of(unit))
.and_then(|scope| scope.get(path))
.copied()
}
pub(crate) fn bind_sources(&mut self, placed: Box<[GraphNodeId]>) {
self.sources = placed;
}
pub(crate) fn fragment_file(&self, fragment: u32) -> Option<GraphNodeId> {
self.sources.get(index_of(fragment)).copied()
}
pub(crate) fn bind_definition(
&mut self,
identity: &Arc<DefinitionIdentity>,
node: GraphNodeId,
) {
self.definitions.insert(Arc::clone(identity), node);
}
pub(crate) fn definition(&self, identity: &DefinitionIdentity) -> Option<GraphNodeId> {
self.definitions.get(identity).copied()
}
pub(crate) fn containment(&self) -> &[ContainmentEdge] {
self.records.containment()
}
pub(crate) fn node_count(&self) -> usize {
self.records.node_count()
}
pub(crate) fn finish(self, tier: ResolutionTier) -> CodeGraph {
self.records.finish(tier)
}
}