Skip to main content

retroglyph_widgets/widget/
meter.rs

1//! [`Meter`]: a load ratio mapped to a green→yellow→red color.
2use retroglyph_core::Color;
3
4/// A load ratio in `0.0..=1.0`, mapped to a green→yellow→red color ramp.
5///
6/// Low load is green, mid load yellow, high load red. Values outside the
7/// range are clamped. Delegates to [`Color::lerp`] (backed by `gem`) rather
8/// than hand-rolling RGB interpolation.
9///
10/// Not a drawing widget -- there's no [`Terminal`](retroglyph_core::Terminal)
11/// involved, just a ratio-to-color mapping -- but kept as its own small
12/// struct rather than a free function so [`Gauge`](super::Gauge),
13/// [`StatBar`](super::StatBar), and [`Sparkline`](super::Sparkline) share
14/// one place that owns the ramp.
15#[derive(Clone, Copy, Debug, PartialEq)]
16pub struct Meter {
17    ratio: f32,
18}
19
20impl Meter {
21    const GREEN: Color = Color::Rgb {
22        r: 80,
23        g: 200,
24        b: 120,
25    };
26    const YELLOW: Color = Color::Rgb {
27        r: 220,
28        g: 200,
29        b: 90,
30    };
31    const RED: Color = Color::Rgb {
32        r: 220,
33        g: 90,
34        b: 90,
35    };
36
37    /// A meter reading `ratio` (clamped to `0.0..=1.0` when colored).
38    #[must_use]
39    pub const fn new(ratio: f32) -> Self {
40        Self { ratio }
41    }
42
43    /// The ramped color for this meter's ratio.
44    #[must_use]
45    pub fn color(self) -> Color {
46        let t = self.ratio.clamp(0.0, 1.0);
47        if t < 0.5 {
48            Color::lerp(Self::GREEN, Self::YELLOW, t * 2.0)
49        } else {
50            Color::lerp(Self::YELLOW, Self::RED, (t - 0.5) * 2.0)
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn low_load_is_green() {
61        assert_eq!(Meter::new(0.0).color(), Meter::GREEN);
62    }
63
64    #[test]
65    fn mid_load_is_yellow() {
66        assert_eq!(Meter::new(0.5).color(), Meter::YELLOW);
67    }
68
69    #[test]
70    fn high_load_is_red() {
71        assert_eq!(Meter::new(1.0).color(), Meter::RED);
72    }
73
74    #[test]
75    fn out_of_range_ratios_are_clamped() {
76        assert_eq!(Meter::new(-1.0).color(), Meter::GREEN);
77        assert_eq!(Meter::new(2.0).color(), Meter::RED);
78    }
79}