1#[must_use]
19pub fn calculate_bandwidth(total_bytes: u64, elapsed_secs: f64) -> f64 {
20 if elapsed_secs > 0.0 {
21 (total_bytes as f64 * 8.0) / elapsed_secs
22 } else {
23 0.0
24 }
25}
26
27#[must_use]
39pub fn determine_stream_count(single: bool) -> usize {
40 if single { 1 } else { 4 }
41}
42
43#[must_use]
53pub fn format_distance(km: f64) -> String {
54 if km < 100.0 {
55 format!("{km:.1} km")
56 } else {
57 format!("{km:.0} km")
58 }
59}
60
61#[must_use]
72pub fn format_data_size(bytes: u64) -> String {
73 if bytes < 1024 * 1024 {
74 format!("{:.1} KB", bytes as f64 / 1024.0)
75 } else if bytes < 1024 * 1024 * 1024 {
76 format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
77 } else {
78 format!("{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
79 }
80}
81
82#[must_use]
92pub fn is_valid_ipv4(s: &str) -> bool {
93 let parts: Vec<&str> = s.split('.').collect();
94 if parts.len() != 4 {
95 return false;
96 }
97 parts.iter().all(|p| p.parse::<u8>().is_ok())
98}
99
100#[must_use]
114pub fn bar_chart(value: f64, max: f64, width: usize) -> String {
115 if max <= 0.0 || width == 0 {
116 return "░".repeat(width);
117 }
118 let pct = (value / max).clamp(0.0, 1.0);
119 let filled = (pct * width as f64).round() as usize;
120 let empty = width.saturating_sub(filled);
121 format!("{}{}", "█".repeat(filled), "░".repeat(empty))
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_calculate_bandwidth_normal() {
130 assert_eq!(calculate_bandwidth(10_000_000, 2.0), 40_000_000.0);
131 }
132
133 #[test]
134 fn test_calculate_bandwidth_zero_elapsed() {
135 assert_eq!(calculate_bandwidth(10_000_000, 0.0), 0.0);
136 }
137
138 #[test]
139 fn test_determine_stream_count_single() {
140 assert_eq!(determine_stream_count(true), 1);
141 }
142
143 #[test]
144 fn test_determine_stream_count_multi() {
145 assert_eq!(determine_stream_count(false), 4);
146 }
147
148 #[test]
149 fn test_format_distance_under_100() {
150 assert_eq!(format_distance(50.5), "50.5 km");
151 assert_eq!(format_distance(99.9), "99.9 km");
152 }
153
154 #[test]
155 fn test_format_distance_100_plus() {
156 assert_eq!(format_distance(100.0), "100 km");
157 assert_eq!(format_distance(150.5), "150 km");
158 }
159
160 #[test]
161 fn test_format_data_size_bytes() {
162 assert!(format_data_size(512).contains("KB"));
163 }
164
165 #[test]
166 fn test_format_data_size_kilobytes() {
167 assert!(format_data_size(500 * 1024).contains("KB"));
168 }
169
170 #[test]
171 fn test_format_data_size_megabytes() {
172 assert!(format_data_size(10 * 1024 * 1024).contains("MB"));
173 }
174
175 #[test]
176 fn test_format_data_size_gigabytes() {
177 assert!(format_data_size(4 * 1024 * 1024 * 1024).contains("GB"));
178 }
179
180 #[test]
181 fn test_is_valid_ipv4_valid() {
182 assert!(is_valid_ipv4("192.168.1.1"));
183 assert!(is_valid_ipv4("0.0.0.0"));
184 assert!(is_valid_ipv4("255.255.255.255"));
185 }
186
187 #[test]
188 fn test_is_valid_ipv4_invalid() {
189 assert!(!is_valid_ipv4("256.1.1.1"));
190 assert!(!is_valid_ipv4("1.2.3"));
191 assert!(!is_valid_ipv4("abc"));
192 assert!(!is_valid_ipv4(""));
193 assert!(!is_valid_ipv4("1.2.3.4.5"));
194 }
195
196 #[test]
197 fn test_bar_chart_half() {
198 let bar = bar_chart(50.0, 100.0, 10);
199 assert_eq!(bar.chars().count(), 10);
200 assert_eq!(bar, "█████░░░░░");
201 }
202
203 #[test]
204 fn test_bar_chart_full() {
205 let bar = bar_chart(100.0, 100.0, 10);
206 assert_eq!(bar.chars().count(), 10);
207 assert_eq!(bar, "██████████");
208 }
209
210 #[test]
211 fn test_bar_chart_empty_val() {
212 let bar = bar_chart(0.0, 100.0, 10);
213 assert_eq!(bar, "░░░░░░░░░░");
214 }
215
216 #[test]
217 fn test_bar_chart_zero_max() {
218 let bar = bar_chart(50.0, 0.0, 10);
219 assert_eq!(bar, "░░░░░░░░░░");
220 }
221
222 #[test]
223 fn test_bar_chart_zero_width() {
224 let bar = bar_chart(50.0, 100.0, 0);
225 assert_eq!(bar, "");
226 }
227
228 #[test]
229 fn test_bar_chart_over_max() {
230 let bar = bar_chart(150.0, 100.0, 10);
231 assert_eq!(bar, "██████████"); }
233}