use std::sync::Arc;
use pedant_core::resolution::rust::{
RustResolutionSnapshot, RustSnapshotFingerprint, RustTargetResolution, RustUnitBinding,
TargetId,
};
use pedant_types::ResolutionReport;
use crate::cache::{
BoundedStore, CacheCategory, CacheCounters, CacheEvent, CachedCodeGraph, CachedGraphState,
GraphCacheLimits, GraphCacheStats,
};
use crate::error::GraphBuildError;
use crate::limits::GraphLimits;
use crate::projection::assembly;
use super::projection;
use super::reuse::ProjectionStore;
struct ExactGraphClaim {
fingerprint: RustSnapshotFingerprint,
root_target: TargetId,
bindings: Box<[RustUnitBinding]>,
report: Arc<ResolutionReport>,
}
impl ExactGraphClaim {
fn new(snapshot: &RustResolutionSnapshot, resolution: &RustTargetResolution) -> Self {
Self {
fingerprint: snapshot.fingerprint(),
root_target: resolution.root_target(),
bindings: resolution.units().into(),
report: resolution.shared_report(),
}
}
fn states_one_report(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.report, &other.report) || self.report == other.report
}
}
impl PartialEq for ExactGraphClaim {
fn eq(&self, other: &Self) -> bool {
self.fingerprint == other.fingerprint
&& self.root_target == other.root_target
&& self.bindings == other.bindings
&& self.states_one_report(other)
}
}
pub struct GraphCache {
counters: Arc<CacheCounters>,
limits: GraphCacheLimits,
graphs: BoundedStore<ExactGraphClaim, Arc<CachedGraphState>>,
projections: ProjectionStore,
}
impl GraphCache {
pub fn new(limits: GraphCacheLimits) -> Self {
let counters = Arc::new(CacheCounters::default());
Self {
graphs: BoundedStore::new(limits.max_exact_graphs()),
projections: ProjectionStore::new(
limits.max_source_projections(),
Arc::clone(&counters),
),
limits,
counters,
}
}
pub fn build_rust_graph(
&mut self,
snapshot: &RustResolutionSnapshot,
resolution: &RustTargetResolution,
limits: GraphLimits,
) -> Result<CachedCodeGraph, GraphBuildError> {
projection::validate(snapshot, resolution)?;
let claim = ExactGraphClaim::new(snapshot, resolution);
match self.graphs.get(&claim) {
Some(retained) => self.reuse(retained, limits),
None => self.build_missing(claim, (snapshot, resolution), limits),
}
}
pub fn stats(&self) -> GraphCacheStats {
self.counters.snapshot()
}
pub fn clear(&mut self) {
self.graphs.clear();
self.projections.clear();
}
fn reuse(
&self,
retained: Arc<CachedGraphState>,
limits: GraphLimits,
) -> Result<CachedCodeGraph, GraphBuildError> {
self.counters
.record(CacheCategory::ExactGraph, CacheEvent::Hit, 1);
retained.graph().admissible(limits)?;
Ok(CachedCodeGraph::new(retained))
}
fn build_missing(
&mut self,
claim: ExactGraphClaim,
stated: (&RustResolutionSnapshot, &RustTargetResolution),
limits: GraphLimits,
) -> Result<CachedCodeGraph, GraphBuildError> {
let (snapshot, resolution) = stated;
self.counters
.record(CacheCategory::ExactGraph, CacheEvent::Miss, 1);
let planned = projection::plan(snapshot, resolution, &mut self.projections)?;
let state = Arc::new(CachedGraphState::new(
assembly::assemble(&planned, limits)?,
self.limits,
Arc::clone(&self.counters),
));
let evicted = self.graphs.retain(claim, Arc::clone(&state));
self.counters
.record(CacheCategory::ExactGraph, CacheEvent::Eviction, evicted);
Ok(CachedCodeGraph::new(state))
}
}