use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak};
use super::event::Observation;
#[derive(Default)]
struct ProbeState {
manifest_reads: RefCell<Vec<Box<str>>>,
source_reads: RefCell<Vec<Box<str>>>,
parses: RefCell<Vec<Box<str>>>,
site_visits: RefCell<Vec<Box<str>>>,
import_walks: RefCell<Vec<Box<str>>>,
capability_projections: RefCell<Vec<Box<str>>>,
semantic_file_setups: RefCell<Vec<Box<str>>>,
semantic_queries: RefCell<Vec<Box<str>>>,
promotions: RefCell<Vec<Box<str>>>,
project_loads: Cell<u32>,
semantic_workspace_loads: Cell<u32>,
dependency_chain_extensions: Cell<u64>,
dependency_chain_history_copies: Cell<u64>,
}
thread_local! {
static INSTALLED: RefCell<Weak<ProbeState>> = const { RefCell::new(Weak::new()) };
}
#[derive(Clone)]
pub struct ResolutionProbe {
state: Rc<ProbeState>,
}
impl ResolutionProbe {
pub fn install() -> Self {
let state = Rc::new(ProbeState::default());
set_installed(Rc::downgrade(&state));
Self { state }
}
pub fn manifest_reads(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.manifest_reads)
}
pub fn source_reads(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.source_reads)
}
pub fn parses(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.parses)
}
pub fn site_visits(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.site_visits)
}
pub fn import_walks(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.import_walks)
}
pub fn capability_projections(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.capability_projections)
}
pub fn project_loads(&self) -> u32 {
self.state.project_loads.get()
}
pub fn dependency_chain_extensions(&self) -> u64 {
self.state.dependency_chain_extensions.get()
}
pub fn dependency_chain_history_copies(&self) -> u64 {
self.state.dependency_chain_history_copies.get()
}
pub fn semantic_workspace_loads(&self) -> u32 {
self.state.semantic_workspace_loads.get()
}
pub fn semantic_file_setups(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.semantic_file_setups)
}
pub fn semantic_queries(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.semantic_queries)
}
pub fn promotions(&self) -> Box<[Box<str>]> {
snapshot_of(&self.state.promotions)
}
}
fn snapshot_of(entries: &RefCell<Vec<Box<str>>>) -> Box<[Box<str>]> {
entries.borrow().iter().cloned().collect()
}
fn set_installed(state: Weak<ProbeState>) {
let replaced = INSTALLED.try_with(|installed| installed.replace(state));
drop(replaced);
}
fn installed() -> Option<Rc<ProbeState>> {
INSTALLED
.try_with(|installed| installed.borrow().upgrade())
.ok()
.flatten()
}
pub(super) fn record(event: &Observation<'_>) {
installed().iter().for_each(|state| apply(state, event));
}
fn apply(state: &Rc<ProbeState>, event: &Observation<'_>) {
match event {
Observation::ProjectLoad => {
state
.project_loads
.set(state.project_loads.get().saturating_add(1));
}
#[cfg(feature = "semantic")]
Observation::SemanticWorkspaceLoad => {
state
.semantic_workspace_loads
.set(state.semantic_workspace_loads.get().saturating_add(1));
}
Observation::ManifestRead(path) => push(&state.manifest_reads, path),
Observation::SourceRead(path) => push(&state.source_reads, path),
Observation::SourceParse(path) => push(&state.parses, path),
Observation::SiteVisit(path) => push(&state.site_visits, path),
Observation::ImportWalk(path) => push(&state.import_walks, path),
Observation::CapabilityProjection(path) => push(&state.capability_projections, path),
Observation::DependencyChainExtension {
history_entries_copied,
} => record_dependency_chain_extension(state, *history_entries_copied),
#[cfg(feature = "semantic")]
Observation::SemanticFileSetup(path) => push(&state.semantic_file_setups, path),
#[cfg(feature = "semantic")]
Observation::SemanticQuery(path) => push(&state.semantic_queries, path),
#[cfg(feature = "semantic")]
Observation::Promotion(path) => push(&state.promotions, path),
}
}
fn record_dependency_chain_extension(state: &ProbeState, history_entries_copied: usize) {
let copied = u64::try_from(history_entries_copied).unwrap_or(u64::MAX);
state
.dependency_chain_extensions
.set(state.dependency_chain_extensions.get().saturating_add(1));
state.dependency_chain_history_copies.set(
state
.dependency_chain_history_copies
.get()
.saturating_add(copied),
);
}
fn push(entries: &RefCell<Vec<Box<str>>>, path: &str) {
entries.borrow_mut().push(Box::from(path));
}