1use crate::elf::FirmwareUsage;
2
3pub fn progress_bar(pct: f64, width: usize) -> String {
14 let fill = if pct <= 0.0 {
15 0
16 } else {
17 let f = (pct / 100.0 * width as f64) as usize;
18 if f < 1 { 1 } else { f }.min(width)
19 };
20 let empty = width - fill;
21 format!("[{}{}]", "\u{2588}".repeat(fill), "\u{2591}".repeat(empty))
22}
23
24pub fn format_bytes(bytes: u64) -> String {
26 if bytes >= 1024 * 1024 {
27 format!("{} MB", bytes / (1024 * 1024))
28 } else if bytes >= 1024 {
29 format!("{} KB", bytes / 1024)
30 } else {
31 format!("{} B", bytes)
32 }
33}
34
35pub fn format_report(usage: &FirmwareUsage, bar_width: usize) -> String {
51 format_report_with_labels(usage, bar_width, "FLASH", "RAM")
52}
53
54pub fn format_report_with_labels(
56 usage: &FirmwareUsage,
57 bar_width: usize,
58 flash_label: &str,
59 ram_label: &str,
60) -> String {
61 let flash_bar = progress_bar(usage.flash_percent(), bar_width);
62 let ram_bar = progress_bar(usage.ram_percent(), bar_width);
63
64 let flash_used_str = format_bytes(usage.flash_used);
65 let flash_total_str = format_bytes(usage.flash_total);
66 let ram_used_str = format_bytes(usage.ram_used);
67 let ram_total_str = format_bytes(usage.ram_total);
68
69 let label_width = flash_label.len().max(ram_label.len());
71
72 let line_flash = format!(
74 " {:<label$} {} {:>5.1}% {:>6} / {:<6} ",
75 flash_label,
76 flash_bar,
77 usage.flash_percent(),
78 flash_used_str,
79 flash_total_str,
80 label = label_width
81 );
82 let line_ram = format!(
83 " {:<label$} {} {:>5.1}% {:>6} / {:<6} ",
84 ram_label,
85 ram_bar,
86 usage.ram_percent(),
87 ram_used_str,
88 ram_total_str,
89 label = label_width
90 );
91
92 let total_width = line_flash.chars().count().max(line_ram.chars().count());
94 let border = format!("+{}+", "-".repeat(total_width));
95
96 let pad = |s: &str, w: usize| {
98 let chars: Vec<char> = s.chars().collect();
99 if chars.len() >= w {
100 chars[..w].iter().collect()
101 } else {
102 format!("{}{}", s, " ".repeat(w - chars.len()))
103 }
104 };
105
106 let mut out = String::new();
107 out.push_str(" [FIRMWARE SIZE]\n");
108 out.push_str(&format!(" {}\n", border));
109 out.push_str(&format!(" |{}|\n", pad(&line_flash, total_width)));
110 out.push_str(&format!(" |{}|\n", pad(&line_ram, total_width)));
111 out.push_str(&format!(" {}", border));
112 out
113}
114
115pub fn print_report<P1: AsRef<std::path::Path>, P2: AsRef<std::path::Path>>(
122 elf_path: P1,
123 memory_x_path: P2,
124) -> Result<(), String> {
125 let config = crate::memory::MemoryConfig::from_file(memory_x_path)?;
126 let usage = crate::elf::analyze_elf(elf_path, &config)?;
127 println!("{}", format_report(&usage, 30));
128 Ok(())
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_progress_bar() {
137 assert_eq!(progress_bar(0.0, 10), "[░░░░░░░░░░]");
138 assert_eq!(progress_bar(100.0, 10), "[██████████]");
139 assert_eq!(progress_bar(50.0, 10), "[█████░░░░░]");
140 }
141
142 #[test]
143 fn test_format_bytes() {
144 assert_eq!(format_bytes(500), "500 B");
145 assert_eq!(format_bytes(1024), "1 KB");
146 assert_eq!(format_bytes(65536), "64 KB");
147 assert_eq!(format_bytes(1048576), "1 MB");
148 }
149
150 #[test]
151 fn test_format_report() {
152 let usage = FirmwareUsage {
153 flash_used: 9933,
154 ram_used: 4,
155 flash_total: 65536,
156 ram_total: 20480,
157 };
158 let report = format_report(&usage, 30);
159 println!("{}", report);
160 assert!(report.contains("FIRMWARE SIZE"));
161 assert!(report.contains("FLASH"));
162 assert!(report.contains("RAM"));
163 }
164}