cu_profiler_report/
table.rs1use crate::model::{Report, scenario_budget, scenario_delta_pct, thousands};
7
8const HEADERS: [&str; 5] = ["Scenario", "Actual CU", "Budget", "Delta", "Status"];
9
10#[must_use]
12pub fn render(report: &Report) -> String {
13 let mut rows: Vec<[String; 5]> = Vec::with_capacity(report.scenarios.len());
14 for s in &report.scenarios {
15 rows.push([
16 s.name.clone(),
17 thousands(s.measurement.total_cu),
18 scenario_budget(s).map_or_else(|| "-".to_string(), thousands),
19 scenario_delta_pct(s).map_or_else(|| "-".to_string(), |d| format!("{d:+.1}%")),
20 s.status.label().to_string(),
21 ]);
22 }
23
24 let mut widths = HEADERS.map(str::len);
26 for row in &rows {
27 for (i, cell) in row.iter().enumerate() {
28 widths[i] = widths[i].max(cell.len());
29 }
30 }
31
32 let mut out = String::new();
33 push_row(&mut out, &HEADERS.map(String::from), &widths);
34 for row in &rows {
35 push_row(&mut out, row, &widths);
36 }
37
38 out.push('\n');
39 let sum = &report.summary;
40 out.push_str(&format!(
41 "{} scenario(s): {} passed, {} warned, {} failed — {} total CU\n",
42 sum.total_scenarios,
43 sum.passed,
44 sum.warned,
45 sum.failed,
46 thousands(sum.total_cu),
47 ));
48 out
49}
50
51fn push_row(out: &mut String, row: &[String; 5], widths: &[usize; 5]) {
52 out.push_str(&pad_left(&row[0], widths[0]));
54 for i in 1..4 {
55 out.push_str(" ");
56 out.push_str(&pad_right(&row[i], widths[i]));
57 }
58 out.push_str(" ");
59 out.push_str(&pad_left(&row[4], widths[4]));
60 while out.ends_with(' ') {
62 out.pop();
63 }
64 out.push('\n');
65}
66
67fn pad_left(s: &str, width: usize) -> String {
68 format!("{s:<width$}")
69}
70
71fn pad_right(s: &str, width: usize) -> String {
72 format!("{s:>width$}")
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use cu_profiler_core::Profiler;
79 use cu_profiler_core::backend::RecordedLogsBackend;
80 use cu_profiler_core::budget::BudgetPolicy;
81 use cu_profiler_core::metadata::RunMetadata;
82 use cu_profiler_core::scenario::Scenario;
83
84 fn sample_report() -> Report {
85 let mut backend = RecordedLogsBackend::new();
86 backend.insert_blob(
87 "swap_exact_in",
88 "Program User111 invoke [1]\n\
89 Program User111 consumed 96812 of 200000 compute units\n\
90 Program User111 success",
91 true,
92 );
93 let mut scenario = Scenario::new("swap_exact_in");
94 scenario.budget = BudgetPolicy {
95 absolute_max_cu: Some(100_000),
96 warn_at_budget_pct: Some(90.0),
97 ..Default::default()
98 };
99 Profiler::new().run(&backend, &[scenario], None, RunMetadata::recorded("0.1.0"))
100 }
101
102 #[test]
103 fn renders_headers_and_values() {
104 let table = render(&sample_report());
105 assert!(table.contains("Scenario"));
106 assert!(table.contains("swap_exact_in"));
107 assert!(table.contains("96,812"));
108 assert!(table.contains("100,000"));
109 assert!(table.contains("WARN"));
110 assert!(table.contains("1 scenario(s): 0 passed, 1 warned, 0 failed"));
111 }
112}