Skip to main content

pedant_graph/cache/
graph.rs

1//! The handle a caller holds on one cached graph.
2
3use std::sync::Arc;
4
5use crate::analysis::index::admitted_counts;
6use crate::analysis::{GraphAnalysisError, GraphAnalysisLimits, GraphEdgeSelection};
7use crate::graph::CodeGraph;
8
9use super::analysis::{CachedGraphAnalysis, DerivedState};
10use super::limits::GraphCacheLimits;
11use super::state::CacheCounters;
12use super::stats::GraphCacheStats;
13
14/// One admitted graph beside everything derived from it.
15///
16/// The derived state belongs to the graph rather than to the cache entry that
17/// retained it, so two handles on one graph share the indexes and the products
18/// either of them produced, and both keep answering after the entry is gone.
19pub(crate) struct CachedGraphState {
20    graph: Arc<CodeGraph>,
21    derived: DerivedState,
22}
23
24impl CachedGraphState {
25    /// One immutable graph, with nothing derived from it yet.
26    pub(crate) fn new(
27        graph: CodeGraph,
28        limits: GraphCacheLimits,
29        counters: Arc<CacheCounters>,
30    ) -> Self {
31        Self {
32            graph: Arc::new(graph),
33            derived: DerivedState::new(limits, counters),
34        }
35    }
36
37    /// The graph this state was built from.
38    pub(crate) fn graph(&self) -> &CodeGraph {
39        &self.graph
40    }
41
42    /// The bounded indexes and products derived from it.
43    pub(crate) fn derived(&self) -> &DerivedState {
44        &self.derived
45    }
46
47    /// What the cache that produced this graph has done so far.
48    ///
49    /// Read through the derived state, which already shares the one set of
50    /// counters this graph records against. A second handle on them here would
51    /// be a second place to keep in step with the first.
52    pub(crate) fn stats(&self) -> GraphCacheStats {
53        self.derived.counters().snapshot()
54    }
55}
56
57/// One immutable graph, shared with the cache that produced it.
58///
59/// Cloning a handle shares the graph rather than copying its records, and a
60/// handle stays valid after its cache entry is evicted, after the cache is
61/// cleared, and after the cache itself is dropped: the memory is the caller's
62/// for as long as the caller holds it.
63#[derive(Clone)]
64pub struct CachedCodeGraph {
65    state: Arc<CachedGraphState>,
66}
67
68impl CachedCodeGraph {
69    /// One handle on an admitted graph, sharing the cache's own state.
70    pub(crate) fn new(state: Arc<CachedGraphState>) -> Self {
71        Self { state }
72    }
73
74    /// The graph this handle holds.
75    pub fn graph(&self) -> &CodeGraph {
76        self.state.graph()
77    }
78
79    /// What the cache that produced this handle has done so far.
80    pub fn stats(&self) -> GraphCacheStats {
81        self.state.stats()
82    }
83
84    /// One shared analysis of this graph under `selection`, reusing the indexes
85    /// this graph already produced for it.
86    ///
87    /// The returned handle answers exactly as [`crate::GraphAnalysis`] answers
88    /// for the same graph, selection, and ceilings, whatever this graph
89    /// retained first.
90    ///
91    /// # Errors
92    ///
93    /// The same refusals [`crate::GraphAnalysis::new`] returns. The node and
94    /// selected-edge counts are proved against the caller's current ceilings
95    /// before any retained index set is examined, so a lowered ceiling refuses
96    /// exactly as it would have refused a fresh analysis.
97    pub fn analyze(
98        &self,
99        selection: GraphEdgeSelection,
100        limits: GraphAnalysisLimits,
101    ) -> Result<CachedGraphAnalysis, GraphAnalysisError> {
102        let admitted = admitted_counts(self.graph(), selection, limits)?;
103        let shared = self
104            .state
105            .derived()
106            .indexed(self.graph(), selection, admitted)?;
107        Ok(CachedGraphAnalysis::new(
108            Arc::clone(&self.state),
109            selection,
110            limits,
111            shared,
112        ))
113    }
114
115    /// Drop the selected indexes and derived products retained for this graph.
116    ///
117    /// Cumulative statistics are not reset, no analysis or product a caller
118    /// holds is invalidated, and the graph itself is untouched: the cache drops
119    /// its own strong references and nothing else.
120    pub fn clear_analysis_cache(&self) {
121        self.state.derived().clear();
122    }
123}