use pedant_types::ResolutionTier;
use serde::Serialize;
use crate::containment::ContainmentEdge;
use crate::edge::{EdgeDraft, GraphEdge, GraphEdgeOrigin};
use crate::error::GraphBuildError;
use crate::id::{GraphEdgeId, GraphNodeId, GraphReferenceId, index_of};
use crate::limits::{GraphCollection, GraphLimits};
use crate::node::{GraphNode, NodeDraft};
use crate::reference::{GraphReference, ReferenceDraft};
#[derive(Serialize, Debug, PartialEq, Eq)]
pub struct CodeGraph {
schema_version: u32,
tier: ResolutionTier,
nodes: Box<[GraphNode]>,
containment: Box<[ContainmentEdge]>,
references: Box<[GraphReference]>,
edges: Box<[GraphEdge]>,
}
impl CodeGraph {
pub const SCHEMA_VERSION: u32 = 1;
pub fn schema_version(&self) -> u32 {
self.schema_version
}
pub fn tier(&self) -> ResolutionTier {
self.tier
}
pub fn nodes(&self) -> &[GraphNode] {
&self.nodes
}
pub fn node(&self, id: GraphNodeId) -> Option<&GraphNode> {
self.nodes.get(index_of(id.index()))
}
pub fn containment(&self) -> &[ContainmentEdge] {
&self.containment
}
pub fn references(&self) -> &[GraphReference] {
&self.references
}
pub fn reference(&self, id: GraphReferenceId) -> Option<&GraphReference> {
self.references.get(index_of(id.index()))
}
pub fn edges(&self) -> &[GraphEdge] {
&self.edges
}
pub fn edge(&self, id: GraphEdgeId) -> Option<&GraphEdge> {
self.edges.get(index_of(id.index()))
}
}
pub(crate) struct RecordCapacity {
pub(crate) nodes: usize,
pub(crate) references: usize,
pub(crate) edges: usize,
}
pub(crate) struct GraphRecords {
limits: GraphLimits,
nodes: Vec<GraphNode>,
containment: Vec<ContainmentEdge>,
references: Vec<(GraphReferenceId, ReferenceDraft)>,
reference_edges: Vec<Vec<GraphEdgeId>>,
edges: Vec<GraphEdge>,
}
impl GraphRecords {
pub(crate) fn new(limits: GraphLimits, capacity: RecordCapacity) -> Self {
let nodes = reserved(capacity.nodes, limits.ceiling(GraphCollection::Node));
let references = reserved(
capacity.references,
limits.ceiling(GraphCollection::Reference),
);
Self {
limits,
nodes: Vec::with_capacity(nodes),
containment: Vec::with_capacity(nodes),
references: Vec::with_capacity(references),
reference_edges: Vec::with_capacity(references),
edges: Vec::with_capacity(reserved(
capacity.edges,
limits.ceiling(GraphCollection::Edge),
)),
}
}
pub(crate) fn insert_node(&mut self, draft: NodeDraft) -> Result<GraphNodeId, GraphBuildError> {
let index = self.admit(GraphCollection::Node)?;
let id = GraphNodeId::new(index);
self.nodes.push(GraphNode::new(id, draft));
Ok(id)
}
pub(crate) fn insert_reference(
&mut self,
draft: ReferenceDraft,
) -> Result<GraphReferenceId, GraphBuildError> {
let index = self.admit(GraphCollection::Reference)?;
let id = GraphReferenceId::new(index);
self.references.push((id, draft));
self.reference_edges.push(Vec::new());
Ok(id)
}
pub(crate) fn insert_edge(&mut self, draft: EdgeDraft) -> Result<GraphEdgeId, GraphBuildError> {
let index = self.admit(GraphCollection::Edge)?;
let id = GraphEdgeId::new(index);
self.link_origin(&draft.origin, id)?;
self.edges.push(GraphEdge::new(id, draft));
Ok(id)
}
fn link_origin(
&mut self,
origin: &GraphEdgeOrigin,
edge: GraphEdgeId,
) -> Result<(), GraphBuildError> {
let reference = match origin {
GraphEdgeOrigin::Reference { reference } => *reference,
GraphEdgeOrigin::Dependency { .. } => return Ok(()),
};
match self.reference_edges.get_mut(index_of(reference.index())) {
Some(produced) => {
produced.push(edge);
Ok(())
}
None => Err(GraphBuildError::MissingReferenceRecord {
reference: reference.index(),
}),
}
}
pub(crate) fn contain(&mut self, parent: GraphNodeId, child: GraphNodeId) {
self.containment.push(ContainmentEdge::new(parent, child));
}
pub(crate) fn containment(&self) -> &[ContainmentEdge] {
&self.containment
}
pub(crate) fn node_count(&self) -> usize {
self.nodes.len()
}
pub(crate) fn finish(mut self, tier: ResolutionTier) -> CodeGraph {
self.containment
.sort_unstable_by_key(|edge| (edge.child(), edge.parent()));
let references = self
.references
.into_iter()
.zip(self.reference_edges)
.map(|((id, draft), edges)| GraphReference::new(id, draft, edges.into_boxed_slice()))
.collect();
CodeGraph {
schema_version: CodeGraph::SCHEMA_VERSION,
tier,
nodes: self.nodes.into_boxed_slice(),
containment: self.containment.into_boxed_slice(),
references,
edges: self.edges.into_boxed_slice(),
}
}
fn admit(&self, collection: GraphCollection) -> Result<u32, GraphBuildError> {
let count = match collection {
GraphCollection::Node => self.nodes.len(),
GraphCollection::Reference => self.references.len(),
GraphCollection::Edge => self.edges.len(),
};
let limit = self.limits.ceiling(collection);
u32::try_from(count)
.ok()
.filter(|next| *next < limit)
.ok_or(GraphBuildError::CapacityExceeded { collection, limit })
}
}
pub(crate) fn reserved(stated: usize, ceiling: u32) -> usize {
stated.min(index_of(ceiling))
}