typst_bake/
stats.rs

1//! Compression statistics for embedded files
2
3/// Compression statistics for all embedded content
4#[derive(Debug, Clone)]
5pub struct EmbedStats {
6    /// Template files statistics
7    pub templates: CategoryStats,
8    /// Package files statistics
9    pub packages: PackageStats,
10    /// Font files statistics
11    pub fonts: CategoryStats,
12}
13
14/// Statistics for a category of files (templates, fonts)
15#[derive(Debug, Clone)]
16pub struct CategoryStats {
17    /// Original uncompressed size in bytes
18    pub original_size: usize,
19    /// Compressed size in bytes
20    pub compressed_size: usize,
21    /// Number of files
22    pub file_count: usize,
23}
24
25/// Statistics for all packages
26#[derive(Debug, Clone)]
27pub struct PackageStats {
28    /// Per-package statistics
29    pub packages: Vec<PackageInfo>,
30    /// Total original size of all packages
31    pub total_original: usize,
32    /// Total compressed size of all packages
33    pub total_compressed: usize,
34}
35
36/// Statistics for a single package
37#[derive(Debug, Clone)]
38pub struct PackageInfo {
39    /// Package name with version (e.g., "gentle-clues:1.2.0")
40    pub name: String,
41    /// Original uncompressed size in bytes
42    pub original_size: usize,
43    /// Compressed size in bytes
44    pub compressed_size: usize,
45    /// Number of files in this package
46    pub file_count: usize,
47}
48
49impl EmbedStats {
50    /// Calculate total original size across all categories
51    pub fn total_original(&self) -> usize {
52        self.templates.original_size + self.packages.total_original + self.fonts.original_size
53    }
54
55    /// Calculate total compressed size across all categories
56    pub fn total_compressed(&self) -> usize {
57        self.templates.compressed_size + self.packages.total_compressed + self.fonts.compressed_size
58    }
59
60    /// Calculate compression ratio (0.0 to 1.0, where 0.0 means no compression)
61    pub fn compression_ratio(&self) -> f64 {
62        let original = self.total_original();
63        if original == 0 {
64            return 0.0;
65        }
66        1.0 - (self.total_compressed() as f64 / original as f64)
67    }
68
69    /// Display compression statistics in a human-readable format
70    pub fn display(&self) {
71        println!("Compression Statistics:");
72        println!("========================");
73
74        // Templates
75        if self.templates.file_count > 0 {
76            println!(
77                "Templates:  {:>9} -> {:>9} ({:>5.1}% reduced, {} files)",
78                format_size(self.templates.original_size),
79                format_size(self.templates.compressed_size),
80                self.templates.compression_ratio() * 100.0,
81                self.templates.file_count
82            );
83        }
84
85        // Fonts
86        if self.fonts.file_count > 0 {
87            println!(
88                "Fonts:      {:>9} -> {:>9} ({:>5.1}% reduced, {} files)",
89                format_size(self.fonts.original_size),
90                format_size(self.fonts.compressed_size),
91                self.fonts.compression_ratio() * 100.0,
92                self.fonts.file_count
93            );
94        }
95
96        // Packages
97        if !self.packages.packages.is_empty() {
98            println!("Packages:");
99
100            // Calculate column widths for package alignment
101            let name_width = self
102                .packages
103                .packages
104                .iter()
105                .map(|p| p.name.len())
106                .max()
107                .unwrap_or(0);
108            let orig_width = self
109                .packages
110                .packages
111                .iter()
112                .map(|p| format_size(p.original_size).len())
113                .max()
114                .unwrap_or(0);
115            let comp_width = self
116                .packages
117                .packages
118                .iter()
119                .map(|p| format_size(p.compressed_size).len())
120                .max()
121                .unwrap_or(0);
122
123            for pkg in &self.packages.packages {
124                println!(
125                    "  {:<name_w$}  {:>orig_w$} -> {:>comp_w$}  ({:>5.1}%)",
126                    pkg.name,
127                    format_size(pkg.original_size),
128                    format_size(pkg.compressed_size),
129                    pkg.compression_ratio() * 100.0,
130                    name_w = name_width,
131                    orig_w = orig_width,
132                    comp_w = comp_width,
133                );
134            }
135        }
136
137        // Total
138        println!("------------------------");
139        println!(
140            "Total: {} -> {} ({:.1}% reduced)",
141            format_size(self.total_original()),
142            format_size(self.total_compressed()),
143            self.compression_ratio() * 100.0
144        );
145    }
146}
147
148impl CategoryStats {
149    /// Calculate compression ratio for this category
150    pub fn compression_ratio(&self) -> f64 {
151        if self.original_size == 0 {
152            return 0.0;
153        }
154        1.0 - (self.compressed_size as f64 / self.original_size as f64)
155    }
156}
157
158impl PackageInfo {
159    /// Calculate compression ratio for this package
160    pub fn compression_ratio(&self) -> f64 {
161        if self.original_size == 0 {
162            return 0.0;
163        }
164        1.0 - (self.compressed_size as f64 / self.original_size as f64)
165    }
166}
167
168impl PackageStats {
169    /// Calculate compression ratio for all packages
170    pub fn compression_ratio(&self) -> f64 {
171        if self.total_original == 0 {
172            return 0.0;
173        }
174        1.0 - (self.total_compressed as f64 / self.total_original as f64)
175    }
176}
177
178/// Format bytes into human-readable size
179fn format_size(bytes: usize) -> String {
180    const KB: usize = 1024;
181    const MB: usize = KB * 1024;
182
183    if bytes >= MB {
184        format!("{:.2} MB", bytes as f64 / MB as f64)
185    } else if bytes >= KB {
186        format!("{:.1} KB", bytes as f64 / KB as f64)
187    } else {
188        format!("{} B", bytes)
189    }
190}