pedant_core/observe/
probe.rs1use std::cell::{Cell, RefCell};
9use std::rc::{Rc, Weak};
10
11use super::event::Observation;
12
13#[derive(Default)]
15struct ProbeState {
16 manifest_reads: RefCell<Vec<Box<str>>>,
17 source_reads: RefCell<Vec<Box<str>>>,
18 parses: RefCell<Vec<Box<str>>>,
19 site_visits: RefCell<Vec<Box<str>>>,
20 import_walks: RefCell<Vec<Box<str>>>,
21 capability_projections: RefCell<Vec<Box<str>>>,
22 semantic_file_setups: RefCell<Vec<Box<str>>>,
23 semantic_queries: RefCell<Vec<Box<str>>>,
24 promotions: RefCell<Vec<Box<str>>>,
25 project_loads: Cell<u32>,
26 semantic_workspace_loads: Cell<u32>,
27 dependency_chain_extensions: Cell<u64>,
28 dependency_chain_history_copies: Cell<u64>,
29}
30
31thread_local! {
32 static INSTALLED: RefCell<Weak<ProbeState>> = const { RefCell::new(Weak::new()) };
36}
37
38#[derive(Clone)]
40pub struct ResolutionProbe {
41 state: Rc<ProbeState>,
42}
43
44impl ResolutionProbe {
45 pub fn install() -> Self {
47 let state = Rc::new(ProbeState::default());
48 set_installed(Rc::downgrade(&state));
49 Self { state }
50 }
51
52 pub fn manifest_reads(&self) -> Box<[Box<str>]> {
54 snapshot_of(&self.state.manifest_reads)
55 }
56
57 pub fn source_reads(&self) -> Box<[Box<str>]> {
59 snapshot_of(&self.state.source_reads)
60 }
61
62 pub fn parses(&self) -> Box<[Box<str>]> {
67 snapshot_of(&self.state.parses)
68 }
69
70 pub fn site_visits(&self) -> Box<[Box<str>]> {
72 snapshot_of(&self.state.site_visits)
73 }
74
75 pub fn import_walks(&self) -> Box<[Box<str>]> {
77 snapshot_of(&self.state.import_walks)
78 }
79
80 pub fn capability_projections(&self) -> Box<[Box<str>]> {
85 snapshot_of(&self.state.capability_projections)
86 }
87
88 pub fn project_loads(&self) -> u32 {
90 self.state.project_loads.get()
91 }
92
93 pub fn dependency_chain_extensions(&self) -> u64 {
95 self.state.dependency_chain_extensions.get()
96 }
97
98 pub fn dependency_chain_history_copies(&self) -> u64 {
101 self.state.dependency_chain_history_copies.get()
102 }
103
104 pub fn semantic_workspace_loads(&self) -> u32 {
106 self.state.semantic_workspace_loads.get()
107 }
108
109 pub fn semantic_file_setups(&self) -> Box<[Box<str>]> {
112 snapshot_of(&self.state.semantic_file_setups)
113 }
114
115 pub fn semantic_queries(&self) -> Box<[Box<str>]> {
117 snapshot_of(&self.state.semantic_queries)
118 }
119
120 pub fn promotions(&self) -> Box<[Box<str>]> {
122 snapshot_of(&self.state.promotions)
123 }
124}
125
126fn snapshot_of(entries: &RefCell<Vec<Box<str>>>) -> Box<[Box<str>]> {
127 entries.borrow().iter().cloned().collect()
128}
129
130fn set_installed(state: Weak<ProbeState>) {
137 let replaced = INSTALLED.try_with(|installed| installed.replace(state));
138 drop(replaced);
139}
140
141fn installed() -> Option<Rc<ProbeState>> {
144 INSTALLED
145 .try_with(|installed| installed.borrow().upgrade())
146 .ok()
147 .flatten()
148}
149
150pub(super) fn record(event: &Observation<'_>) {
152 installed().iter().for_each(|state| apply(state, event));
153}
154
155fn apply(state: &Rc<ProbeState>, event: &Observation<'_>) {
156 match event {
157 Observation::ProjectLoad => {
158 state
159 .project_loads
160 .set(state.project_loads.get().saturating_add(1));
161 }
162 #[cfg(feature = "semantic")]
163 Observation::SemanticWorkspaceLoad => {
164 state
165 .semantic_workspace_loads
166 .set(state.semantic_workspace_loads.get().saturating_add(1));
167 }
168 Observation::ManifestRead(path) => push(&state.manifest_reads, path),
169 Observation::SourceRead(path) => push(&state.source_reads, path),
170 Observation::SourceParse(path) => push(&state.parses, path),
171 Observation::SiteVisit(path) => push(&state.site_visits, path),
172 Observation::ImportWalk(path) => push(&state.import_walks, path),
173 Observation::CapabilityProjection(path) => push(&state.capability_projections, path),
174 Observation::DependencyChainExtension {
175 history_entries_copied,
176 } => record_dependency_chain_extension(state, *history_entries_copied),
177 #[cfg(feature = "semantic")]
178 Observation::SemanticFileSetup(path) => push(&state.semantic_file_setups, path),
179 #[cfg(feature = "semantic")]
180 Observation::SemanticQuery(path) => push(&state.semantic_queries, path),
181 #[cfg(feature = "semantic")]
182 Observation::Promotion(path) => push(&state.promotions, path),
183 }
184}
185
186fn record_dependency_chain_extension(state: &ProbeState, history_entries_copied: usize) {
187 let copied = u64::try_from(history_entries_copied).unwrap_or(u64::MAX);
188 state
189 .dependency_chain_extensions
190 .set(state.dependency_chain_extensions.get().saturating_add(1));
191 state.dependency_chain_history_copies.set(
192 state
193 .dependency_chain_history_copies
194 .get()
195 .saturating_add(copied),
196 );
197}
198
199fn push(entries: &RefCell<Vec<Box<str>>>, path: &str) {
200 entries.borrow_mut().push(Box::from(path));
201}