use std::sync::atomic::{AtomicU8, AtomicU32, Ordering};
use obs_proto::obs::v1::Severity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Interest {
Unknown = 0,
Never = 1,
Sometimes = 2,
Always = 3,
}
impl Interest {
fn from_u8(value: u8) -> Self {
match value {
1 => Self::Never,
2 => Self::Sometimes,
3 => Self::Always,
_ => Self::Unknown,
}
}
}
#[derive(Debug)]
pub struct ObsCallsite {
full_name: &'static str,
default_sev: Severity,
module: &'static str,
file: &'static str,
line: u32,
interest: AtomicU8,
cached_gen: AtomicU32,
}
impl ObsCallsite {
#[must_use]
pub const fn new(
full_name: &'static str,
default_sev: Severity,
module: &'static str,
file: &'static str,
line: u32,
) -> Self {
Self {
full_name,
default_sev,
module,
file,
line,
interest: AtomicU8::new(Interest::Unknown as u8),
cached_gen: AtomicU32::new(0),
}
}
#[inline(always)]
#[must_use]
pub fn enabled(&self, current_gen: u32) -> EnabledOutcome {
let cached_gen = self.cached_gen.load(Ordering::Relaxed);
if cached_gen != current_gen {
return EnabledOutcome::ReProbe;
}
match Interest::from_u8(self.interest.load(Ordering::Relaxed)) {
Interest::Unknown => EnabledOutcome::ReProbe,
Interest::Never => EnabledOutcome::Off,
Interest::Sometimes => EnabledOutcome::SometimesOn,
Interest::Always => EnabledOutcome::AlwaysOn,
}
}
pub fn cache(&self, interest: Interest, current_gen: u32) {
self.interest.store(interest as u8, Ordering::Relaxed);
self.cached_gen.store(current_gen, Ordering::Relaxed);
}
pub fn reset_cache(&self) {
self.interest
.store(Interest::Unknown as u8, Ordering::Relaxed);
self.cached_gen.store(0, Ordering::Relaxed);
}
#[must_use]
pub const fn full_name(&self) -> &'static str {
self.full_name
}
#[must_use]
pub const fn default_sev(&self) -> Severity {
self.default_sev
}
#[must_use]
pub const fn module(&self) -> &'static str {
self.module
}
#[must_use]
pub const fn file(&self) -> &'static str {
self.file
}
#[must_use]
pub const fn line(&self) -> u32 {
self.line
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum EnabledOutcome {
Off,
SometimesOn,
AlwaysOn,
ReProbe,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_start_in_unknown() {
static CS: ObsCallsite = ObsCallsite::new(
"test.v1.ProbeUnknown",
Severity::Info,
module_path!(),
file!(),
line!(),
);
CS.reset_cache();
assert_eq!(CS.enabled(1), EnabledOutcome::ReProbe);
}
#[test]
fn test_should_short_circuit_on_never() {
static CS: ObsCallsite = ObsCallsite::new(
"test.v1.ProbeNever",
Severity::Info,
module_path!(),
file!(),
line!(),
);
CS.cache(Interest::Never, 7);
assert_eq!(CS.enabled(7), EnabledOutcome::Off);
}
#[test]
fn test_should_reprobe_on_generation_mismatch() {
static CS: ObsCallsite = ObsCallsite::new(
"test.v1.ProbeReprobe",
Severity::Info,
module_path!(),
file!(),
line!(),
);
CS.cache(Interest::Always, 7);
assert_eq!(CS.enabled(7), EnabledOutcome::AlwaysOn);
assert_eq!(CS.enabled(8), EnabledOutcome::ReProbe);
}
}