use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_core::resolution::rust::RustSnapshotUnitId;
use pedant_types::ResolutionTier;
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;
struct UnitScope {
owner: u32,
container: GraphNodeId,
files: BTreeMap<Arc<str>, GraphNodeId>,
}
struct BoundUnit {
container: GraphNodeId,
snapshot_unit: RustSnapshotUnitId,
}
pub(crate) struct ProjectionCapacity {
pub(crate) units: usize,
pub(crate) definitions: usize,
pub(crate) references: usize,
pub(crate) edges: usize,
}
impl ProjectionCapacity {
fn records(&self) -> RecordCapacity {
RecordCapacity {
nodes: self.units.saturating_add(self.definitions),
references: self.references,
edges: self.edges,
}
}
}
pub(crate) struct ProjectionState {
records: GraphRecords,
scopes: BTreeMap<RustSnapshotUnitId, UnitScope>,
units: Vec<BoundUnit>,
definitions: Vec<GraphNodeId>,
}
impl ProjectionState {
pub(crate) fn new(limits: GraphLimits, capacity: ProjectionCapacity) -> Self {
let ceiling = limits.ceiling(GraphCollection::Node);
Self {
records: GraphRecords::new(limits, capacity.records()),
scopes: BTreeMap::new(),
units: Vec::with_capacity(reserved(capacity.units, ceiling)),
definitions: Vec::with_capacity(reserved(capacity.definitions, ceiling)),
}
}
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_unit(
&mut self,
container: GraphNodeId,
unit: (RustSnapshotUnitId, u32),
) -> Option<u32> {
let (snapshot_unit, reported) = unit;
match self.scopes.get(&snapshot_unit) {
Some(held) => Some(held.owner),
None => {
self.scopes.insert(
snapshot_unit,
UnitScope {
owner: reported,
container,
files: BTreeMap::new(),
},
);
self.units.push(BoundUnit {
container,
snapshot_unit,
});
None
}
}
}
pub(crate) fn container(&self, unit: u32) -> Option<GraphNodeId> {
self.units.get(index_of(unit)).map(|bound| bound.container)
}
pub(crate) fn snapshot_container(&self, unit: RustSnapshotUnitId) -> Option<GraphNodeId> {
self.scopes.get(&unit).map(|scope| scope.container)
}
pub(crate) fn snapshot_unit(&self, unit: u32) -> Option<RustSnapshotUnitId> {
self.units
.get(index_of(unit))
.map(|bound| bound.snapshot_unit)
}
pub(crate) fn qualify_source(
&mut self,
snapshot_unit: RustSnapshotUnitId,
path: &Arc<str>,
draft: NodeDraft,
) -> Result<Option<GraphNodeId>, GraphBuildError> {
match self.file_node(snapshot_unit, path) {
Some(found) => Ok(Some(found)),
None => self.instantiate_source(snapshot_unit, path, draft),
}
}
fn instantiate_source(
&mut self,
snapshot_unit: RustSnapshotUnitId,
path: &Arc<str>,
draft: NodeDraft,
) -> Result<Option<GraphNodeId>, GraphBuildError> {
match self.scopes.get_mut(&snapshot_unit) {
Some(scope) => {
let node = self.records.insert_node(draft)?;
scope.files.insert(Arc::clone(path), node);
Ok(Some(node))
}
None => Ok(None),
}
}
pub(crate) fn file_node(&self, unit: RustSnapshotUnitId, path: &str) -> Option<GraphNodeId> {
self.scopes
.get(&unit)
.and_then(|scope| scope.files.get(path))
.copied()
}
pub(crate) fn bind_definition(&mut self, node: GraphNodeId) {
self.definitions.push(node);
}
pub(crate) fn definition_node(&self, definition: u32) -> Option<GraphNodeId> {
self.definitions.get(index_of(definition)).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)
}
}