Skip to main content

pedant_graph/
id.rs

1//! Dense typed identities into one graph's collections.
2//!
3//! One identity shape, branded three ways: a node position can never be read as
4//! a reference or an edge position, and the value semantics, serialization, and
5//! `index()` form cannot drift between them. Each is a transparent `u32` with no
6//! public constructor, so a caller reads an identity out of a graph and indexes
7//! back into the same graph but cannot manufacture one that names nothing.
8
9use std::marker::PhantomData;
10
11use serde::Serialize;
12
13/// One dense `u32` position whose marker fixes the collection it addresses.
14#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[repr(transparent)]
16#[serde(transparent)]
17pub struct GraphId<K> {
18    index: u32,
19    #[serde(skip)]
20    collection: PhantomData<fn() -> K>,
21}
22
23impl<K> GraphId<K> {
24    pub(crate) fn new(index: u32) -> Self {
25        Self {
26            index,
27            collection: PhantomData,
28        }
29    }
30
31    /// This identity's position in the collection it addresses.
32    pub fn index(self) -> u32 {
33        self.index
34    }
35}
36
37/// Marker distinguishing node identities from the other identity kinds.
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct NodeIdKind;
40
41/// Marker distinguishing reference identities from the other identity kinds.
42#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct ReferenceIdKind;
44
45/// Marker distinguishing edge identities from the other identity kinds.
46#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub struct EdgeIdKind;
48
49/// A node's dense identity within one [`crate::CodeGraph`].
50pub type GraphNodeId = GraphId<NodeIdKind>;
51
52/// A reference record's dense identity within one [`crate::CodeGraph`].
53pub type GraphReferenceId = GraphId<ReferenceIdKind>;
54
55/// An edge's dense identity within one [`crate::CodeGraph`].
56pub type GraphEdgeId = GraphId<EdgeIdKind>;
57
58/// The slice position one stored identity names.
59///
60/// Every identity this crate stores is a `u32`, and every slice it reads is
61/// addressed by `usize`. On a target where `usize` is narrower, the saturating
62/// answer selects no element, which each caller already treats as absence.
63pub(crate) fn index_of(value: u32) -> usize {
64    usize::try_from(value).unwrap_or(usize::MAX)
65}
66
67/// The stored identity one slice position is recorded as.
68///
69/// The saturating answer names no record, and every caller reads it as absence:
70/// a diagnostic payload prints the ceiling value, and a lookup made with it
71/// selects nothing. Minting paths never reach the saturation, because
72/// [`crate::GraphLimits`] proves the configured and fixed-width ceilings first;
73/// a position taken from a supplied collection, such as a snapshot dependency
74/// edge index, is bounded by no ceiling and may reach it.
75pub(crate) fn position(value: usize) -> u32 {
76    u32::try_from(value).unwrap_or(u32::MAX)
77}