1use std::any::TypeId;
2
3use crate::{
4 counter::{
5 BytesCount, BytesFormat, CharsCount, CyclesCount, IntoCounter, ItemsCount, MaxCountUInt,
6 },
7 time::FineDuration,
8 util::{fmt::DisplayThroughput, ty::TypeCast},
9};
10
11/// Type-erased `Counter`.
12///
13/// This does not implement `Copy` because in the future it will contain
14/// user-defined counters.
15#[derive(Clone)]
16pub(crate) struct AnyCounter {
17 pub kind: KnownCounterKind,
18 count: MaxCountUInt,
19}
20
21impl AnyCounter {
22 #[inline]
23 pub(crate) fn new<C: IntoCounter>(counter: C) -> Self {
24 let counter = counter.into_counter();
25
26 if let Some(bytes) = counter.cast_ref::<BytesCount>() {
27 Self::bytes(bytes.count)
28 } else if let Some(chars) = counter.cast_ref::<CharsCount>() {
29 Self::chars(chars.count)
30 } else if let Some(cycles) = counter.cast_ref::<CyclesCount>() {
31 Self::cycles(cycles.count)
32 } else if let Some(items) = counter.cast_ref::<ItemsCount>() {
33 Self::items(items.count)
34 } else {
35 unreachable!()
36 }
37 }
38
39 #[inline]
40 pub(crate) fn known(kind: KnownCounterKind, count: MaxCountUInt) -> Self {
41 Self { kind, count }
42 }
43
44 #[inline]
45 pub(crate) fn bytes(count: MaxCountUInt) -> Self {
46 Self::known(KnownCounterKind::Bytes, count)
47 }
48
49 #[inline]
50 pub(crate) fn chars(count: MaxCountUInt) -> Self {
51 Self::known(KnownCounterKind::Chars, count)
52 }
53
54 #[inline]
55 pub(crate) fn cycles(count: MaxCountUInt) -> Self {
56 Self::known(KnownCounterKind::Cycles, count)
57 }
58
59 #[inline]
60 pub(crate) fn items(count: MaxCountUInt) -> Self {
61 Self::known(KnownCounterKind::Items, count)
62 }
63
64 pub(crate) fn display_throughput(
65 &self,
66 duration: FineDuration,
67 bytes_format: BytesFormat,
68 ) -> DisplayThroughput {
69 DisplayThroughput { counter: self, picos: duration.picos as f64, bytes_format }
70 }
71
72 #[inline]
73 pub(crate) fn count(&self) -> MaxCountUInt {
74 self.count
75 }
76
77 #[inline]
78 pub(crate) fn known_kind(&self) -> KnownCounterKind {
79 self.kind
80 }
81}
82
83/// Kind of `Counter` defined by this crate.
84#[derive(Clone, Copy, PartialEq, Eq, Debug)]
85pub(crate) enum KnownCounterKind {
86 Bytes,
87 Chars,
88 Cycles,
89 Items,
90}
91
92impl KnownCounterKind {
93 pub const COUNT: usize = 4;
94
95 pub const ALL: [Self; Self::COUNT] = [Self::Bytes, Self::Chars, Self::Cycles, Self::Items];
96
97 /// The maximum width for columns displaying counters.
98 pub const MAX_COMMON_COLUMN_WIDTH: usize = "1.111 Kitem/s".len();
99
100 #[inline]
101 pub fn of<C: IntoCounter>() -> Self {
102 let id: TypeId = TypeId::of::<C::Counter>();
103 if id == TypeId::of::<BytesCount>() {
104 Self::Bytes
105 } else if id == TypeId::of::<CharsCount>() {
106 Self::Chars
107 } else if id == TypeId::of::<CyclesCount>() {
108 Self::Cycles
109 } else if id == TypeId::of::<ItemsCount>() {
110 Self::Items
111 } else {
112 unreachable!()
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn known_counter_kind() {
123 macro_rules! test {
124 ($t:ident, $k:ident) => {
125 assert_eq!(KnownCounterKind::of::<$t>(), KnownCounterKind::$k);
126 };
127 }
128
129 test!(BytesCount, Bytes);
130 test!(CharsCount, Chars);
131 test!(CyclesCount, Cycles);
132 test!(ItemsCount, Items);
133 }
134
135 mod display_throughput {
136 use super::*;
137
138 #[test]
139 fn bytes() {
140 #[track_caller]
141 fn test(
142 bytes: MaxCountUInt,
143 picos: u128,
144 expected_binary: &str,
145 expected_decimal: &str,
146 ) {
147 for (bytes_format, expected) in [
148 (BytesFormat::Binary, expected_binary),
149 (BytesFormat::Decimal, expected_decimal),
150 ] {
151 assert_eq!(
152 AnyCounter::bytes(bytes)
153 .display_throughput(FineDuration { picos }, bytes_format)
154 .to_string(),
155 expected
156 );
157 }
158 }
159
160 #[track_caller]
161 fn test_all(bytes: MaxCountUInt, picos: u128, expected: &str) {
162 test(bytes, picos, expected, expected);
163 }
164
165 test_all(1, 0, "inf B/s");
166 test_all(MaxCountUInt::MAX, 0, "inf B/s");
167
168 test_all(0, 0, "0 B/s");
169 test_all(0, 1, "0 B/s");
170 test_all(0, u128::MAX, "0 B/s");
171 }
172
173 #[test]
174 fn chars() {
175 #[track_caller]
176 fn test(chars: MaxCountUInt, picos: u128, expected: &str) {
177 assert_eq!(
178 AnyCounter::chars(chars)
179 .display_throughput(FineDuration { picos }, BytesFormat::default())
180 .to_string(),
181 expected
182 );
183 }
184
185 test(1, 0, "inf char/s");
186 test(MaxCountUInt::MAX, 0, "inf char/s");
187
188 test(0, 0, "0 char/s");
189 test(0, 1, "0 char/s");
190 test(0, u128::MAX, "0 char/s");
191 }
192
193 #[test]
194 fn cycles() {
195 #[track_caller]
196 fn test(cycles: MaxCountUInt, picos: u128, expected: &str) {
197 assert_eq!(
198 AnyCounter::cycles(cycles)
199 .display_throughput(FineDuration { picos }, BytesFormat::default())
200 .to_string(),
201 expected
202 );
203 }
204
205 test(1, 0, "inf Hz");
206 test(MaxCountUInt::MAX, 0, "inf Hz");
207
208 test(0, 0, "0 Hz");
209 test(0, 1, "0 Hz");
210 test(0, u128::MAX, "0 Hz");
211 }
212
213 #[test]
214 fn items() {
215 #[track_caller]
216 fn test(items: MaxCountUInt, picos: u128, expected: &str) {
217 assert_eq!(
218 AnyCounter::items(items)
219 .display_throughput(FineDuration { picos }, BytesFormat::default())
220 .to_string(),
221 expected
222 );
223 }
224
225 test(1, 0, "inf item/s");
226 test(MaxCountUInt::MAX, 0, "inf item/s");
227
228 test(0, 0, "0 item/s");
229 test(0, 1, "0 item/s");
230 test(0, u128::MAX, "0 item/s");
231 }
232 }
233}
234