fret_ui_headless/table/
aggregation.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum Aggregation {
8 #[default]
9 None,
10 Count,
11 SumU64,
12 MinU64,
13 MaxU64,
14 MeanU64,
15}
16
17pub fn aggregate_u64(
18 agg: Aggregation,
19 mut values: impl Iterator<Item = u64>,
20 count: usize,
21) -> Option<u64> {
22 match agg {
23 Aggregation::None => None,
24 Aggregation::Count => Some(count as u64),
25 Aggregation::SumU64 => values.try_fold(0u64, |acc, v| acc.checked_add(v)),
26 Aggregation::MinU64 => values.min(),
27 Aggregation::MaxU64 => values.max(),
28 Aggregation::MeanU64 => {
29 if count == 0 {
30 return None;
31 }
32 let sum = values.try_fold(0u64, |acc, v| acc.checked_add(v))?;
33 Some(sum / (count as u64))
34 }
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn count_ignores_values() {
44 assert_eq!(
45 aggregate_u64(Aggregation::Count, [1, 2, 3].into_iter(), 10),
46 Some(10)
47 );
48 }
49
50 #[test]
51 fn sum_checked() {
52 assert_eq!(
53 aggregate_u64(Aggregation::SumU64, [1, 2, 3].into_iter(), 3),
54 Some(6)
55 );
56 assert_eq!(
57 aggregate_u64(Aggregation::SumU64, [u64::MAX, 1].into_iter(), 2),
58 None
59 );
60 }
61
62 #[test]
63 fn min_max() {
64 assert_eq!(
65 aggregate_u64(Aggregation::MinU64, [3, 2, 1].into_iter(), 3),
66 Some(1)
67 );
68 assert_eq!(
69 aggregate_u64(Aggregation::MaxU64, [3, 2, 1].into_iter(), 3),
70 Some(3)
71 );
72 assert_eq!(aggregate_u64(Aggregation::MinU64, [].into_iter(), 0), None);
73 assert_eq!(aggregate_u64(Aggregation::MaxU64, [].into_iter(), 0), None);
74 }
75
76 #[test]
77 fn mean_uses_count_and_is_checked() {
78 assert_eq!(
79 aggregate_u64(Aggregation::MeanU64, [10, 20].into_iter(), 2),
80 Some(15)
81 );
82 assert_eq!(
83 aggregate_u64(Aggregation::MeanU64, [10].into_iter(), 0),
84 None
85 );
86 assert_eq!(
87 aggregate_u64(Aggregation::MeanU64, [u64::MAX, 1].into_iter(), 2),
88 None
89 );
90 }
91}