use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug)]
pub struct SystemPressure {
headroom_bits: AtomicU32,
}
impl SystemPressure {
#[must_use]
#[inline]
pub fn new() -> Self {
Self {
headroom_bits: AtomicU32::new(1.0_f32.to_bits()),
}
}
#[must_use]
#[inline]
pub fn with_headroom(headroom: f32) -> Self {
let clamped = headroom.clamp(0.0, 1.0);
Self {
headroom_bits: AtomicU32::new(clamped.to_bits()),
}
}
#[must_use]
#[inline]
pub fn headroom(&self) -> f32 {
f32::from_bits(self.headroom_bits.load(Ordering::Relaxed))
}
#[inline]
pub fn set_headroom(&self, headroom: f32) {
let clamped = headroom.clamp(0.0, 1.0);
self.headroom_bits
.store(clamped.to_bits(), Ordering::Relaxed);
}
#[must_use]
#[inline]
pub fn should_degrade(&self, threshold: f32) -> bool {
self.headroom() < threshold
}
#[must_use]
#[inline]
pub fn degradation_level(&self) -> u8 {
let h = self.headroom();
if h > 0.875 {
0
} else if h > 0.625 {
1
} else if h > 0.375 {
2
} else if h > 0.125 {
3
} else {
4
}
}
#[must_use]
#[inline]
pub fn level_label(&self) -> &'static str {
match self.degradation_level() {
0 => "normal",
1 => "light",
2 => "moderate",
3 => "heavy",
_ => "emergency",
}
}
}
impl Default for SystemPressure {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{Value, json};
fn scrub_pressure_json(value: Value) -> Value {
let mut scrubbed = value;
if let Some(headroom) = scrubbed.pointer_mut("/headroom") {
let formatted = headroom
.as_f64()
.map_or_else(|| headroom.to_string(), |value| format!("{value:.2}"));
*headroom = Value::String(formatted);
}
scrubbed
}
#[test]
fn new_starts_at_full_headroom() {
let p = SystemPressure::new();
assert!((p.headroom() - 1.0).abs() < f32::EPSILON);
assert_eq!(p.degradation_level(), 0);
assert_eq!(p.level_label(), "normal");
}
#[test]
fn set_and_read_headroom() {
let p = SystemPressure::new();
p.set_headroom(0.42);
assert!((p.headroom() - 0.42).abs() < 0.001);
}
#[test]
fn headroom_clamped() {
let p = SystemPressure::new();
p.set_headroom(1.5);
assert!((p.headroom() - 1.0).abs() < f32::EPSILON);
p.set_headroom(-0.3);
assert!(p.headroom().abs() < f32::EPSILON);
}
#[test]
fn degradation_levels() {
let p = SystemPressure::new();
p.set_headroom(0.9);
assert_eq!(p.degradation_level(), 0);
p.set_headroom(0.8);
assert_eq!(p.degradation_level(), 1);
p.set_headroom(0.5);
assert_eq!(p.degradation_level(), 2);
p.set_headroom(0.25);
assert_eq!(p.degradation_level(), 3);
p.set_headroom(0.0);
assert_eq!(p.degradation_level(), 4);
}
#[test]
fn should_degrade_threshold() {
let p = SystemPressure::with_headroom(0.3);
assert!(p.should_degrade(0.5));
assert!(!p.should_degrade(0.2));
}
#[test]
fn with_headroom_constructor() {
let p = SystemPressure::with_headroom(0.7);
assert!((p.headroom() - 0.7).abs() < 0.001);
}
#[test]
fn level_labels() {
let p = SystemPressure::new();
p.set_headroom(0.9);
assert_eq!(p.level_label(), "normal");
p.set_headroom(0.75);
assert_eq!(p.level_label(), "light");
p.set_headroom(0.5);
assert_eq!(p.level_label(), "moderate");
p.set_headroom(0.25);
assert_eq!(p.level_label(), "heavy");
p.set_headroom(0.0);
assert_eq!(p.level_label(), "emergency");
}
#[test]
fn pressure_json_snapshot_scrubbed() {
let p = SystemPressure::with_headroom(0.42);
insta::assert_json_snapshot!(
"pressure_json_scrubbed",
scrub_pressure_json(json!({
"headroom": p.headroom(),
"degradation_level": p.degradation_level(),
"label": p.level_label(),
"should_degrade_0_5": p.should_degrade(0.5),
}))
);
}
}