pedant-graph 0.4.0

Deterministic code topology projected from pedant's symbol-resolution results
Documentation
//! Deterministic, language-tagged code topology built from one snapshot-bound
//! symbol-resolution result.
//!
//! The graph separates three relations a consumer would otherwise have to
//! reconstruct itself: logical containment, source association, and
//! certainty-carrying reference and dependency edges. It consumes already
//! validated in-memory facts and widens nothing: it loads no project, reads no
//! file, parses no source, invokes no build tool, and evaluates no policy.
//!
//! The snapshot must be the one the resolution was validated against, and a
//! successful build serializes to the compact version-1 JSON described below
//! through `serde_json::to_string`.
//!
//! ```no_run
//! # use pedant_core::resolution::rust::{RustResolutionSnapshot, RustTargetResolution};
//! # use pedant_graph::{CodeGraph, GraphBuildError};
//! # fn topology(
//! #     snapshot: &RustResolutionSnapshot,
//! #     resolution: &RustTargetResolution,
//! # ) -> Result<CodeGraph, GraphBuildError> {
//! pedant_graph::build_rust_graph(snapshot, resolution)
//! # }
//! ```
//!
//! # A second language behind a feature
//!
//! The default build projects Rust. Selecting the `go` feature adds the Go
//! adapter beside it: `build_go_graph` and `build_go_graph_with_limits` — named
//! here rather than linked, because the default build has no such item —
//! take a Go snapshot and the resolution validated against it and answer the
//! same [`CodeGraph`] the Rust builders answer, through the same checked
//! assembler. The feature forwards to `pedant-core/go-resolution` and nothing
//! else, so a build without it links no Go grammar through this crate, and the
//! optional graph cache below stays Rust-only either way.
//!
//! # The version-1 shape
//!
//! One object with six keys, in this order:
//!
//! - `schema_version`: the integer [`CodeGraph::SCHEMA_VERSION`].
//! - `tier`: `"syntactic"` or `"semantic"`, taken from the supplied report.
//! - `nodes`: `id`, `language`, `name`, `kind`, `location`. `kind` is tagged by
//!   `category`; `location` is `null` or tagged by `kind` as `file` or `span`.
//! - `containment`: `parent` and `child`, sorted by child then parent.
//! - `references`: `id`, `source`, `language`, `kind`, `text`, `span`, `gaps`,
//!   `edges`, in mint order.
//! - `edges`: `id`, `source`, `target`, `kind`, `certainty`, `origin`, in mint
//!   order. `origin` is tagged by `kind` as `reference` or `dependency`.
//!
//! Every identity is an integer equal to the record's position in its own
//! slice, and every graph-owned enum is one lower-snake-case string. No key
//! states a coordinate, a size, a color, or any other layout choice.

//! # Bounded queries over a built graph
//!
//! A caller states which edges to admit and what the answer may cost, then
//! asks. Nothing about the graph changes.
//!
//! ```no_run
//! # use pedant_graph::{
//! #     CodeGraph, GraphAnalysis, GraphAnalysisError, GraphAnalysisLimits, GraphDirection,
//! #     GraphEdgeSelection, GraphNeighbor, GraphNodeId,
//! # };
//! # fn callers_of(
//! #     graph: &CodeGraph,
//! #     node: GraphNodeId,
//! # ) -> Result<Box<[GraphNeighbor]>, GraphAnalysisError> {
//! let analysis = GraphAnalysis::new(
//!     graph,
//!     GraphEdgeSelection::code_relations(),
//!     GraphAnalysisLimits::new(100_000, 400_000, 8, 50_000_000),
//! )?;
//! analysis.neighbors(node, 2, GraphDirection::Incoming)
//! # }
//! ```
//!
//! The same view answers the derived questions. Cohesion, modularity, affinity,
//! and boundary-crossing cycles are evidence about the partition the graph
//! declares; layout metadata is that evidence beside the degrees and weights a
//! ranker or a renderer would otherwise measure itself.
//!
//! ```no_run
//! # use pedant_graph::{
//! #     CodeGraph, GraphAnalysis, GraphAnalysisError, GraphAnalysisLimits, GraphEdgeSelection,
//! #     LayoutAssistMetadata,
//! # };
//! # fn described(graph: &CodeGraph) -> Result<LayoutAssistMetadata, GraphAnalysisError> {
//! let analysis = GraphAnalysis::new(
//!     graph,
//!     GraphEdgeSelection::code_relations(),
//!     GraphAnalysisLimits::new(100_000, 400_000, 8, 50_000_000),
//! )?;
//! let evidence = analysis.divergence();
//! for module in evidence.cohesion() {
//!     let _ = (module.root(), module.internal_edges(), module.score());
//! }
//! analysis.layout_assist()
//! # }
//! ```
//!
//! # Optional bounded reuse
//!
//! A caller that inspects successive repository states may keep a cache of the
//! work it already admitted. The cache builds nothing of its own: it takes the
//! same snapshot and validated resolution the direct builder takes, returns the
//! same graph, and states how many values it may retain. Zero disables a
//! category without disabling the computation behind it.
//!
//! ```no_run
//! # use pedant_core::resolution::rust::{RustResolutionSnapshot, RustTargetResolution};
//! # use pedant_graph::{
//! #     GraphAnalysisError, GraphAnalysisLimits, GraphBuildError, GraphCache, GraphCacheLimits,
//! #     GraphEdgeSelection, GraphLimits,
//! # };
//! # #[derive(Debug)]
//! # enum ReuseError {
//! #     Build(GraphBuildError),
//! #     Analysis(GraphAnalysisError),
//! # }
//! # impl From<GraphBuildError> for ReuseError {
//! #     fn from(error: GraphBuildError) -> Self { Self::Build(error) }
//! # }
//! # impl From<GraphAnalysisError> for ReuseError {
//! #     fn from(error: GraphAnalysisError) -> Self { Self::Analysis(error) }
//! # }
//! # fn reused(
//! #     snapshot: &RustResolutionSnapshot,
//! #     resolution: &RustTargetResolution,
//! # ) -> Result<(), ReuseError> {
//! let mut cache = GraphCache::new(GraphCacheLimits::new(4_096, 8, 4, 256));
//! let held = cache.build_rust_graph(snapshot, resolution, GraphLimits::default())?;
//! let analysis = held.analyze(
//!     GraphEdgeSelection::code_relations(),
//!     GraphAnalysisLimits::new(100_000, 400_000, 8, 50_000_000),
//! )?;
//! let _ = (analysis.divergence(), cache.stats().exact_graph_hits());
//! # Ok(())
//! # }
//! ```
//!
//! The returned handles are the caller's: a graph, an analysis, and every
//! product they answer with stay valid and immutable after eviction, after
//! `clear`, and after the cache itself is dropped. Nothing about the version-1
//! object above changes, and no cache value is serialized.

#![deny(missing_docs)]

/// Derived analysis over one borrowed graph.
mod analysis;
/// Optional caller-owned reuse of already admitted graph work.
mod cache;
/// The logical hierarchy relation.
mod containment;
/// Graph edges, their certainty, and their evidence.
mod edge;
/// Why a graph build refused.
mod error;
/// The Go projection adapter.
#[cfg(feature = "go")]
mod go;
/// The immutable graph value and its checked record store.
mod graph;
/// Dense typed identities into one graph's collections.
mod id;
/// The configured ceilings a build refuses at.
mod limits;
/// Graph nodes, their categories, and their source locations.
mod node;
/// The one language-neutral projection every graph is minted through.
mod projection;
/// Reference records and what they denote.
mod reference;
/// The Rust projection adapter.
mod rust;

pub use analysis::{
    BetweennessCentrality, BoundaryCrossingComponent, ComponentIdKind, CondensationEdge,
    CondensationGraph, DeclaredModulePartition, DegreeCentrality, GraphAnalysis,
    GraphAnalysisError, GraphAnalysisLimits, GraphComponent, GraphComponentId, GraphComponents,
    GraphDirection, GraphDivergence, GraphEdgeSelection, GraphNeighbor, GraphPath, GraphSubgraph,
    LayoutAssistMetadata, LayoutEdgeMetadata, LayoutNodeMetadata, MisplacementCandidate,
    ModuleCohesion,
};
pub use cache::{CachedCodeGraph, CachedGraphAnalysis, GraphCacheLimits, GraphCacheStats};
pub use containment::ContainmentEdge;
pub use edge::{
    DependencyEvidence, GraphCertainty, GraphDependencyKind, GraphEdge, GraphEdgeKind,
    GraphEdgeOrigin,
};
pub use error::GraphBuildError;
#[cfg(feature = "go")]
pub use go::{build_go_graph, build_go_graph_with_limits};
pub use graph::CodeGraph;
pub use id::{
    EdgeIdKind, GraphEdgeId, GraphId, GraphNodeId, GraphReferenceId, NodeIdKind, ReferenceIdKind,
};
pub use limits::{GraphCollection, GraphLimits};
pub use node::{GraphNode, GraphNodeKind, GraphNodeLocation};
pub use reference::{GraphReference, GraphReferenceKind};
pub use rust::{GraphCache, build_rust_graph, build_rust_graph_with_limits};