use std::collections::{BTreeMap, HashMap};
use std::hash::{BuildHasherDefault, DefaultHasher};
use std::sync::Arc;
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;
use super::identity::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 {
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: Vec<GraphNodeId>,
files: Vec<SourceScope>,
definitions: DefinitionScope,
}
impl ProjectionState {
pub(crate) fn new(limits: GraphLimits, capacity: ProjectionCapacity) -> Self {
let ceiling = limits.ceiling(GraphCollection::Node);
let units = reserved(capacity.units, ceiling);
let definitions = reserved(capacity.definitions, ceiling);
Self {
records: GraphRecords::new(limits, capacity.records()),
containers: Vec::with_capacity(units),
files: Vec::with_capacity(units),
definitions: DefinitionScope::with_capacity_and_hasher(
definitions,
BuildHasherDefault::default(),
),
}
}
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, container: GraphNodeId) {
self.containers.push(container);
self.files.push(SourceScope::new());
}
pub(crate) fn container(&self, unit: u32) -> Option<GraphNodeId> {
self.containers.get(index_of(unit)).copied()
}
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_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)
}
}