fluvio_system_util/counters/
counters.rs1use std::cmp;
7use std::collections::BTreeMap;
8
9#[derive(Debug, PartialEq)]
10pub struct Counters<T> {
11 pub list: BTreeMap<T, Counter>,
12}
13
14#[derive(Debug, PartialEq)]
15pub struct Counter {
16 label: &'static str,
17 internal: bool,
18 value: u32,
19}
20
21impl<T> ::std::default::Default for Counters<T>
22where
23 T: Ord,
24{
25 fn default() -> Self {
26 Self {
27 list: BTreeMap::new(),
28 }
29 }
30}
31
32impl<T> Counters<T>
33where
34 T: Ord,
35{
36 pub fn new(items: Vec<(T, &'static str, bool)>) -> Self {
38 let mut counters = Counters::default();
39 for (id, label, internal) in items {
40 counters.list.insert(
41 id,
42 Counter {
43 label,
44 internal,
45 value: 0,
46 },
47 );
48 }
49 counters
50 }
51
52 pub fn inc_counter(&mut self, id: T) {
54 if let Some(counter) = self.list.get_mut(&id) {
55 counter.value += 1;
56 }
57 }
58
59 pub fn set_counter(&mut self, id: T, val: u32) {
61 if let Some(counter) = self.list.get_mut(&id) {
62 counter.value = val;
63 }
64 }
65
66 pub fn reset(&mut self) {
68 for counter in self.list.values_mut() {
69 counter.value = 0;
70 }
71 }
72
73 pub fn header_fmt(&self) -> String {
75 let mut res = String::new();
76
77 for counter in self.list.values() {
79 res.push_str(&format!("{:^10}", counter.label));
80 res.push_str(" ");
81 }
82
83 if res.len() > 2 {
85 res.truncate(res.len() - 2);
86 }
87
88 res
89 }
90
91 pub fn values_fmt(&self) -> String {
93 let mut res = String::new();
94
95 for counter in self.list.values() {
97 let value_str = counter.value.to_string();
98 let space_width = cmp::max(counter.label.len(), 10);
99 let value_width = value_str.len();
100
101 let (pad_left, pad_right) = if value_width > space_width {
103 (0, 0)
104 } else {
105 let pad_left = (space_width - value_width) / 2;
106 let pad_right = space_width - pad_left - value_width;
107 (pad_left, pad_right)
108 };
109
110 res.push_str(&(0..pad_left).map(|_| " ").collect::<String>());
111 res.push_str(&value_str);
112 res.push_str(&(0..pad_right).map(|_| " ").collect::<String>());
113 res.push_str(" ");
114 }
115
116 if res.len() > 2 {
118 res.truncate(res.len() - 2);
119 }
120
121 res
122 }
123}
124
125#[cfg(test)]
130pub mod test {
131 use super::*;
132
133 #[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
134 enum TestCntr {
135 Ok = 0,
136 Failed = 1,
137 Retry = 2,
138 Shutdown = 3,
139 InternalErr = 4,
140 }
141
142 fn generate_counters() -> Vec<(TestCntr, &'static str, bool)> {
143 vec![
144 (TestCntr::Ok, "CONN-OK", false),
145 (TestCntr::Failed, "CONN-FAILED", false),
146 (TestCntr::Retry, "CONN-RETRY", false),
147 (TestCntr::Shutdown, "CONN-SHUTDOWN", false),
148 (TestCntr::InternalErr, "INTERNAL-ERR", true),
149 ]
150 }
151
152 #[test]
153 fn test_counters_all() {
154 let mut counters = Counters::new(generate_counters());
155
156 assert_eq!(counters.list.len(), 5);
158
159 let header = counters.header_fmt();
161 let expected_header =
162 " CONN-OK CONN-FAILED CONN-RETRY CONN-SHUTDOWN INTERNAL-ERR".to_owned();
163 assert_eq!(header, expected_header);
164
165 counters.set_counter(TestCntr::Ok, 4294967290);
167 counters.inc_counter(TestCntr::Failed);
168 counters.set_counter(TestCntr::Retry, 4199999999);
169 counters.inc_counter(TestCntr::Shutdown);
170 counters.inc_counter(TestCntr::Shutdown);
171 let values = counters.values_fmt();
172 let expected_values =
173 "4294967290 1 4199999999 2 0 ".to_owned();
174 assert_eq!(values, expected_values);
175
176 let mut expected_counters = Counters::new(generate_counters());
178 expected_counters.set_counter(TestCntr::Ok, 4294967290);
179 expected_counters.set_counter(TestCntr::Failed, 1);
180 expected_counters.set_counter(TestCntr::Retry, 4199999999);
181 expected_counters.set_counter(TestCntr::Shutdown, 2);
182 assert_eq!(counters, expected_counters);
183
184 counters.reset();
186 assert_eq!(counters, Counters::new(generate_counters()));
187 assert_eq!(counters.list.len(), 5);
188 }
189}