redact-core 0.12.6

Core PII detection and anonymization engine
Documentation
// Copyright 2026 Censgate LLC.
// Licensed under the Apache License, Version 2.0. See the LICENSE file
// in the project root for license information.

//! Stage-span gate for `/v1/redact` children.
//!
//! Recognizers live below the gateway crate, so they cannot see `TraceLevel`.
//! The gateway enables this thread-local for the duration of a pass when
//! operations tracing is on. Call sites still use `tracing::info_span!` with a
//! literal name so the tracing macros stay valid.

use std::cell::Cell;

thread_local! {
    static OPERATIONS: Cell<bool> = const { Cell::new(false) };
}

/// Run `f` with stage spans enabled or disabled for this thread.
pub fn with_operation_spans<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
    struct Restore(bool);
    impl Drop for Restore {
        fn drop(&mut self) {
            OPERATIONS.with(|flag| flag.set(self.0));
        }
    }
    let prev = OPERATIONS.with(|flag| flag.replace(enabled));
    let _restore = Restore(prev);
    f()
}

/// Whether the current pass should emit detect-stage child spans.
pub fn operations_enabled() -> bool {
    OPERATIONS.with(Cell::get)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn off_by_default() {
        assert!(!operations_enabled());
    }

    #[test]
    fn on_inside_scope() {
        with_operation_spans(true, || {
            assert!(operations_enabled());
        });
        assert!(!operations_enabled());
    }

    #[test]
    fn restores_after_panic() {
        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
            with_operation_spans(true, || panic!("boom"));
        }));
        assert!(!operations_enabled());
    }
}