Skip to main content

ic_metrics/summary/
mod.rs

1//! Saturating arithmetic without sampling or attribution policy.
2
3#[cfg(test)]
4mod tests;
5
6/// Record one value into a sample count and total, saturating independently.
7///
8/// Zero is a completed sample. `value` and `total` must use the same unit.
9/// This primitive supports consumer-owned report shapes without serialization
10/// dependencies. It does not validate continuity or make saturated counters
11/// suitable for exact interval arithmetic.
12pub const fn record_sample(samples: &mut u64, total: &mut u64, value: u64) {
13    *samples = samples.saturating_add(1);
14    *total = total.saturating_add(value);
15}
16
17/// Saturating summary of observations in one consumer-selected unit.
18///
19/// Count and total saturate independently at `u64::MAX`. Treat either counter
20/// at that value as unavailable for exact interval arithmetic, including when
21/// reached exactly. Latest and maximum remain individual observations after
22/// saturation. Empty and measured zero are distinct states.
23///
24/// Consumers establish identity and reset boundaries before comparing snapshots.
25/// The summary does not decide whether observations overlap or are additive.
26#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
27pub struct MeasurementSummary {
28    samples: u64,
29    total: u64,
30    latest: u64,
31    maximum: u64,
32}
33
34impl MeasurementSummary {
35    /// Empty summary, with no latest or maximum observation.
36    pub const EMPTY: Self = Self {
37        samples: 0,
38        total: 0,
39        latest: 0,
40        maximum: 0,
41    };
42
43    /// Record one completed observation, including zero.
44    ///
45    /// Every observation must use the same unit as earlier observations.
46    pub const fn record(&mut self, value: u64) {
47        record_sample(&mut self.samples, &mut self.total, value);
48        self.latest = value;
49        if value > self.maximum {
50            self.maximum = value;
51        }
52    }
53
54    /// Number of completed samples, saturating independently of the total.
55    #[must_use]
56    pub const fn samples(self) -> u64 {
57        self.samples
58    }
59
60    /// Sum of all observations, saturating independently of the sample count.
61    #[must_use]
62    pub const fn total(self) -> u64 {
63        self.total
64    }
65
66    /// Latest observation, or `None` when no sample has been recorded.
67    #[must_use]
68    pub const fn latest(self) -> Option<u64> {
69        if self.samples == 0 {
70            None
71        } else {
72            Some(self.latest)
73        }
74    }
75
76    /// Largest observation, or `None` when no sample has been recorded.
77    #[must_use]
78    pub const fn maximum(self) -> Option<u64> {
79        if self.samples == 0 {
80            None
81        } else {
82            Some(self.maximum)
83        }
84    }
85}