Skip to main content

omena_parser/
instrumentation.rs

1//! Lightweight instrumentation for parser materialization.
2//!
3//! The counters in this module support regression gates that verify parser
4//! consumers do not accidentally rematerialize token streams.
5
6use std::cell::Cell;
7#[cfg(test)]
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10use serde::Serialize;
11
12#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct OmenaParserLexInstrumentationSnapshotV0 {
15    pub lex_invocation_count: u64,
16    pub lex_token_count: u64,
17}
18
19#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
20#[serde(rename_all = "camelCase")]
21pub struct OmenaParserParseInstrumentationSnapshotV0 {
22    pub parse_invocation_count: u64,
23    pub parse_token_count: u64,
24}
25
26thread_local! {
27    static LEX_INSTRUMENTATION: Cell<Option<OmenaParserLexInstrumentationSnapshotV0>> =
28        const { Cell::new(None) };
29    static PARSE_INSTRUMENTATION: Cell<Option<OmenaParserParseInstrumentationSnapshotV0>> =
30        const { Cell::new(None) };
31    #[cfg(test)]
32    static SYNTAX_ROOT_MATERIALIZATION_COUNT: AtomicUsize =
33        const { AtomicUsize::new(0) };
34}
35
36pub fn with_omena_parser_lex_instrumentation<T>(
37    operation: impl FnOnce() -> T,
38) -> (T, OmenaParserLexInstrumentationSnapshotV0) {
39    LEX_INSTRUMENTATION.with(|instrumentation| {
40        let previous =
41            instrumentation.replace(Some(OmenaParserLexInstrumentationSnapshotV0::default()));
42        let value = operation();
43        let snapshot = instrumentation
44            .replace(previous)
45            .unwrap_or_else(OmenaParserLexInstrumentationSnapshotV0::default);
46        (value, snapshot)
47    })
48}
49
50pub fn with_omena_parser_parse_instrumentation<T>(
51    operation: impl FnOnce() -> T,
52) -> (T, OmenaParserParseInstrumentationSnapshotV0) {
53    PARSE_INSTRUMENTATION.with(|instrumentation| {
54        let previous =
55            instrumentation.replace(Some(OmenaParserParseInstrumentationSnapshotV0::default()));
56        let value = operation();
57        let snapshot = instrumentation
58            .replace(previous)
59            .unwrap_or_else(OmenaParserParseInstrumentationSnapshotV0::default);
60        (value, snapshot)
61    })
62}
63
64pub(crate) fn record_omena_parser_lex_materialization(token_count: usize) {
65    LEX_INSTRUMENTATION.with(|instrumentation| {
66        if let Some(mut snapshot) = instrumentation.get() {
67            snapshot.lex_invocation_count += 1;
68            snapshot.lex_token_count += token_count as u64;
69            instrumentation.set(Some(snapshot));
70        }
71    });
72}
73
74pub(crate) fn record_omena_parser_parse_materialization(token_count: usize) {
75    PARSE_INSTRUMENTATION.with(|instrumentation| {
76        if let Some(mut snapshot) = instrumentation.get() {
77            snapshot.parse_invocation_count += 1;
78            snapshot.parse_token_count += token_count as u64;
79            instrumentation.set(Some(snapshot));
80        }
81    });
82}
83
84#[cfg(test)]
85pub(crate) fn record_omena_parser_syntax_root_materialization() {
86    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.fetch_add(1, Ordering::SeqCst));
87}
88
89#[cfg(not(test))]
90pub(crate) fn record_omena_parser_syntax_root_materialization() {}
91
92#[cfg(test)]
93pub(crate) fn reset_omena_parser_syntax_root_materialization_count() {
94    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.store(0, Ordering::SeqCst));
95}
96
97#[cfg(test)]
98pub(crate) fn omena_parser_syntax_root_materialization_count() -> usize {
99    SYNTAX_ROOT_MATERIALIZATION_COUNT.with(|counter| counter.load(Ordering::SeqCst))
100}