1#[derive(Debug, Clone)]
10pub struct EmbedStats {
11 pub templates: CategoryStats,
13 pub packages: PackageStats,
15 pub fonts: CategoryStats,
17}
18
19#[derive(Debug, Clone)]
21pub struct CategoryStats {
22 pub original_size: usize,
24 pub compressed_size: usize,
26 pub file_count: usize,
28}
29
30#[derive(Debug, Clone)]
32pub struct PackageStats {
33 pub packages: Vec<PackageInfo>,
35 pub total_original: usize,
37 pub total_compressed: usize,
39}
40
41#[derive(Debug, Clone)]
43pub struct PackageInfo {
44 pub name: String,
46 pub original_size: usize,
48 pub compressed_size: usize,
50 pub file_count: usize,
52}
53
54impl EmbedStats {
55 pub fn total_original(&self) -> usize {
57 self.templates.original_size + self.packages.total_original + self.fonts.original_size
58 }
59
60 pub fn total_compressed(&self) -> usize {
62 self.templates.compressed_size + self.packages.total_compressed + self.fonts.compressed_size
63 }
64
65 pub fn compression_ratio(&self) -> f64 {
67 let original = self.total_original();
68 if original == 0 {
69 return 0.0;
70 }
71 1.0 - (self.total_compressed() as f64 / original as f64)
72 }
73
74 pub fn display(&self) {
76 println!("Compression Statistics:");
77 println!("========================");
78
79 if self.templates.file_count > 0 {
81 println!(
82 "Templates: {:>9} -> {:>9} ({:>5.1}% reduced, {} files)",
83 format_size(self.templates.original_size),
84 format_size(self.templates.compressed_size),
85 self.templates.compression_ratio() * 100.0,
86 self.templates.file_count
87 );
88 }
89
90 if self.fonts.file_count > 0 {
92 println!(
93 "Fonts: {:>9} -> {:>9} ({:>5.1}% reduced, {} files)",
94 format_size(self.fonts.original_size),
95 format_size(self.fonts.compressed_size),
96 self.fonts.compression_ratio() * 100.0,
97 self.fonts.file_count
98 );
99 }
100
101 if !self.packages.packages.is_empty() {
103 println!("Packages:");
104
105 let name_width = self
107 .packages
108 .packages
109 .iter()
110 .map(|p| p.name.len())
111 .max()
112 .unwrap_or(0);
113 let orig_width = self
114 .packages
115 .packages
116 .iter()
117 .map(|p| format_size(p.original_size).len())
118 .max()
119 .unwrap_or(0);
120 let comp_width = self
121 .packages
122 .packages
123 .iter()
124 .map(|p| format_size(p.compressed_size).len())
125 .max()
126 .unwrap_or(0);
127
128 for pkg in &self.packages.packages {
129 println!(
130 " {:<name_w$} {:>orig_w$} -> {:>comp_w$} ({:>5.1}%)",
131 pkg.name,
132 format_size(pkg.original_size),
133 format_size(pkg.compressed_size),
134 pkg.compression_ratio() * 100.0,
135 name_w = name_width,
136 orig_w = orig_width,
137 comp_w = comp_width,
138 );
139 }
140 }
141
142 println!("------------------------");
144 println!(
145 "Total: {} -> {} ({:.1}% reduced)",
146 format_size(self.total_original()),
147 format_size(self.total_compressed()),
148 self.compression_ratio() * 100.0
149 );
150 }
151}
152
153impl CategoryStats {
154 pub fn compression_ratio(&self) -> f64 {
156 if self.original_size == 0 {
157 return 0.0;
158 }
159 1.0 - (self.compressed_size as f64 / self.original_size as f64)
160 }
161}
162
163impl PackageInfo {
164 pub fn compression_ratio(&self) -> f64 {
166 if self.original_size == 0 {
167 return 0.0;
168 }
169 1.0 - (self.compressed_size as f64 / self.original_size as f64)
170 }
171}
172
173impl PackageStats {
174 pub fn compression_ratio(&self) -> f64 {
176 if self.total_original == 0 {
177 return 0.0;
178 }
179 1.0 - (self.total_compressed as f64 / self.total_original as f64)
180 }
181}
182
183fn format_size(bytes: usize) -> String {
185 const KB: usize = 1024;
186 const MB: usize = KB * 1024;
187
188 if bytes >= MB {
189 format!("{:.2} MB", bytes as f64 / MB as f64)
190 } else if bytes >= KB {
191 format!("{:.1} KB", bytes as f64 / KB as f64)
192 } else {
193 format!("{} B", bytes)
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_format_size_bytes() {
203 assert_eq!(format_size(0), "0 B");
204 assert_eq!(format_size(512), "512 B");
205 assert_eq!(format_size(1023), "1023 B");
206 }
207
208 #[test]
209 fn test_format_size_kilobytes() {
210 assert_eq!(format_size(1024), "1.0 KB");
211 assert_eq!(format_size(1536), "1.5 KB");
212 assert_eq!(format_size(10240), "10.0 KB");
213 }
214
215 #[test]
216 fn test_format_size_megabytes() {
217 assert_eq!(format_size(1048576), "1.00 MB");
218 assert_eq!(format_size(1572864), "1.50 MB");
219 }
220
221 #[test]
222 fn test_compression_ratio_zero_original() {
223 let stats = CategoryStats {
224 original_size: 0,
225 compressed_size: 0,
226 file_count: 0,
227 };
228 assert_eq!(stats.compression_ratio(), 0.0);
229 }
230
231 #[test]
232 fn test_compression_ratio_75_percent() {
233 let stats = CategoryStats {
237 original_size: 1000,
238 compressed_size: 250,
239 file_count: 1,
240 };
241 assert!((stats.compression_ratio() - 0.75).abs() < 0.001);
242 }
243
244 #[test]
245 fn test_embed_stats_totals() {
246 let stats = EmbedStats {
247 templates: CategoryStats {
248 original_size: 1000,
249 compressed_size: 200, file_count: 1,
251 },
252 fonts: CategoryStats {
253 original_size: 2000,
254 compressed_size: 600, file_count: 2,
256 },
257 packages: PackageStats {
258 packages: vec![],
259 total_original: 1000,
260 total_compressed: 200, },
262 };
263 assert_eq!(stats.total_original(), 4000);
265 assert_eq!(stats.total_compressed(), 1000);
266 assert!((stats.compression_ratio() - 0.75).abs() < 0.001);
267 }
268}