Skip to content

Commit bd0755a

Browse files
feat: clean up group headers and emoji usage, make the executor title not dim after run
1 parent c40186d commit bd0755a

8 files changed

Lines changed: 119 additions & 70 deletions

File tree

src/cli/run/helpers/benchmark_display.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ fn build_simulation_table(results: &[&FetchLocalRunBenchmarkResult]) -> String {
159159
}
160160
})
161161
.collect();
162-
build_table_with_style(&rows, "CPU Simulation", "\u{2699}")
162+
build_table_with_style(
163+
&rows,
164+
ExecutorName::Valgrind.label(),
165+
ExecutorName::Valgrind.icon(),
166+
)
163167
}
164168

165169
fn build_walltime_table(results: &[&FetchLocalRunBenchmarkResult]) -> String {
@@ -197,7 +201,11 @@ fn build_walltime_table(results: &[&FetchLocalRunBenchmarkResult]) -> String {
197201
}
198202
})
199203
.collect();
200-
build_table_with_style(&rows, "Walltime", "\u{23F1}")
204+
build_table_with_style(
205+
&rows,
206+
ExecutorName::WallTime.label(),
207+
ExecutorName::WallTime.icon(),
208+
)
201209
}
202210

203211
fn build_memory_table(results: &[&FetchLocalRunBenchmarkResult]) -> String {
@@ -231,7 +239,11 @@ fn build_memory_table(results: &[&FetchLocalRunBenchmarkResult]) -> String {
231239
}
232240
})
233241
.collect();
234-
build_table_with_style(&rows, "Memory", "\u{2630}")
242+
build_table_with_style(
243+
&rows,
244+
ExecutorName::Memory.label(),
245+
ExecutorName::Memory.icon(),
246+
)
235247
}
236248

237249
pub fn build_benchmark_table(results: &[FetchLocalRunBenchmarkResult]) -> String {

src/cli/run/helpers/snapshots/codspeed_runner__cli__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ source: src/cli/run/helpers/benchmark_display.rs
33
expression: table
44
---
55
╭─────────────────────────────────────────────────────────────────╮
6-
CPU Simulation
6+
CPU Simulation
77
├─────────────────┬─────────┬────────┬───────┬────────┬───────────┤
88
BenchmarkTimeInstr. │ CacheMemorySys. Time
99
├─────────────────┼─────────┼────────┼───────┼────────┼───────────┤
1010
bench_parse1.23 ms85.0%10.0%4.0%12.34 µs
1111
bench_serialize2.57 ms70.0%20.0%8.0%51.34 µs
1212
╰─────────────────┴─────────┴────────┴───────┴────────┴───────────╯
1313
╭─────────────────────────────────────────────────────────────────────╮
14-
Walltime
14+
Walltime
1515
├────────────────────┬─────────────┬────────────┬────────┬────────────┤
1616
BenchmarkTime (best) │ IterationsStdDevTotal time
1717
├────────────────────┼─────────────┼────────────┼────────┼────────────┤
1818
bench_http_request150.00 ms1005.00%150.00 ms
1919
bench_db_query25.00 ms5002.00%25.00 ms
2020
╰────────────────────┴─────────────┴────────────┴────────┴────────────╯
2121
╭─────────────────────────────────────────────────────────────────╮
22-
Memory
22+
Memory
2323
├───────────────────┬─────────────┬─────────────────┬─────────────┤
2424
BenchmarkPeak memoryTotal allocatedAllocations
2525
├───────────────────┼─────────────┼─────────────────┼─────────────┤

src/cli/setup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub async fn setup(setup_cache_dir: Option<&Path>) -> Result<()> {
1010
for executor in executors {
1111
info!(
1212
"Setting up the environment for the executor: {}",
13-
executor.name().to_string()
13+
executor.name()
1414
);
1515
executor.setup(&system_info, setup_cache_dir).await?;
1616
}

src/executor/interfaces.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde::{Deserialize, Serialize};
2+
use std::fmt;
23

34
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)]
45
#[serde(rename_all = "lowercase")]
@@ -8,13 +9,32 @@ pub enum ExecutorName {
89
Memory,
910
}
1011

11-
#[allow(clippy::to_string_trait_impl)]
12-
impl ToString for ExecutorName {
13-
fn to_string(&self) -> String {
12+
impl fmt::Display for ExecutorName {
13+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1414
match self {
15-
ExecutorName::Valgrind => "valgrind".to_string(),
16-
ExecutorName::WallTime => "walltime".to_string(),
17-
ExecutorName::Memory => "memory".to_string(),
15+
ExecutorName::Valgrind => write!(f, "valgrind"),
16+
ExecutorName::WallTime => write!(f, "walltime"),
17+
ExecutorName::Memory => write!(f, "memory"),
18+
}
19+
}
20+
}
21+
22+
impl ExecutorName {
23+
/// Human-readable label for this executor.
24+
pub fn label(&self) -> &'static str {
25+
match self {
26+
ExecutorName::Valgrind => "CPU Simulation",
27+
ExecutorName::WallTime => "Walltime",
28+
ExecutorName::Memory => "Memory",
29+
}
30+
}
31+
32+
/// Nerd Font icon for this executor.
33+
pub fn icon(&self) -> &'static str {
34+
match self {
35+
ExecutorName::Valgrind => "\u{f4bc}",
36+
ExecutorName::WallTime => "\u{f520}",
37+
ExecutorName::Memory => "\u{efc5}",
1838
}
1939
}
2040
}

src/executor/orchestrator.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,17 @@ impl Orchestrator {
128128
let run_parts: Vec<ExecutorTarget> = command_labels
129129
.iter()
130130
.flat_map(|(cmd, label)| {
131-
modes.iter().map(move |mode| ExecutorTarget {
132-
command: cmd.clone(),
133-
mode,
134-
label: format!("[{mode}] {label}"),
131+
modes.iter().map(move |mode| {
132+
let executor_name = get_executor_from_mode(mode).name();
133+
ExecutorTarget {
134+
command: cmd.clone(),
135+
mode,
136+
label: format!(
137+
"{} {} - {label}",
138+
executor_name.icon(),
139+
executor_name.label()
140+
),
141+
}
135142
})
136143
})
137144
.collect();
@@ -145,11 +152,11 @@ impl Orchestrator {
145152

146153
for (run_part_index, part) in run_parts.into_iter().enumerate() {
147154
let config = self.config.executor_config_for_command(part.command);
155+
let executor = get_executor_from_mode(part.mode);
148156
let profile_folder =
149-
self.resolve_profile_folder(part.mode, run_part_index, total_parts)?;
157+
self.resolve_profile_folder(&executor.name(), run_part_index, total_parts)?;
150158

151159
let ctx = ExecutionContext::new(config, profile_folder);
152-
let executor = get_executor_from_mode(part.mode);
153160

154161
activate_rolling_buffer(&part.label);
155162

@@ -172,18 +179,18 @@ impl Orchestrator {
172179
/// Resolve the profile folder for a given run part.
173180
///
174181
/// - Single run part + user-specified folder: use as-is
175-
/// - Multiple run parts + user-specified folder: `<folder>/<mode>-<index>`
182+
/// - Multiple run parts + user-specified folder: `<folder>/<executor>-<index>`
176183
/// - No user-specified folder: create a random temp folder
177184
fn resolve_profile_folder(
178185
&self,
179-
mode: &RunnerMode,
186+
executor_name: &ExecutorName,
180187
run_part_index: usize,
181188
total_parts: usize,
182189
) -> Result<PathBuf> {
183190
match (&self.config.profile_folder, total_parts) {
184191
(Some(folder), 1) => Ok(folder.clone()),
185192
(Some(folder), _) => {
186-
let subfolder = folder.join(format!("{mode}-{run_part_index}"));
193+
let subfolder = folder.join(format!("{executor_name}-{run_part_index}"));
187194
std::fs::create_dir_all(&subfolder).with_context(|| {
188195
format!(
189196
"Failed to create profile subfolder: {}",

src/local_logger/mod.rs

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -72,38 +72,48 @@ impl Log for LocalLogger {
7272

7373
if let Some(group_event) = get_group_event(record) {
7474
match group_event {
75-
GroupEvent::Start(name) | GroupEvent::StartOpened(name) => {
76-
let header = format_group_header(&name);
77-
eprintln!("\n{header}");
75+
GroupEvent::Start(ref name) | GroupEvent::StartOpened(ref name) => {
76+
let opened = matches!(group_event, GroupEvent::StartOpened(_));
77+
let name = name.clone();
7878

79-
// Store current group name for completion message
80-
if let Ok(mut current) = CURRENT_GROUP_NAME.lock() {
81-
*current = Some(name.clone());
82-
}
79+
let header = format_group_header(&name);
80+
eprintln!();
81+
eprintln!("{header}");
82+
eprintln!();
83+
84+
// Opened groups don't get a spinner or closing checkmark
85+
if !opened {
86+
// Store current group name for completion message
87+
if let Ok(mut current) = CURRENT_GROUP_NAME.lock() {
88+
*current = Some(name.clone());
89+
}
8390

84-
if *IS_TTY {
85-
let spinner = ProgressBar::new_spinner();
86-
let tick_strings: Vec<String> = SPINNER_TICKS
87-
.iter()
88-
.map(|s| format!("{}", style(s).color256(CODSPEED_U8_COLOR_CODE).dim()))
89-
.collect();
90-
let tick_strs: Vec<&str> =
91-
tick_strings.iter().map(|s| s.as_str()).collect();
92-
93-
spinner.set_style(
94-
ProgressStyle::with_template(
95-
&format!(
96-
" {{spinner}} {{wide_msg:.{CODSPEED_U8_COLOR_CODE}}} {{elapsed:.dim}}"
97-
),
98-
)
99-
.unwrap()
100-
.tick_strings(&tick_strs),
101-
);
102-
spinner.set_message(format!("{name}..."));
103-
spinner.enable_steady_tick(Duration::from_millis(300));
104-
SPINNER.lock().unwrap().replace(spinner);
105-
} else {
106-
eprintln!("{name}...");
91+
if *IS_TTY {
92+
let spinner = ProgressBar::new_spinner();
93+
let tick_strings: Vec<String> = SPINNER_TICKS
94+
.iter()
95+
.map(|s| {
96+
format!("{}", style(s).color256(CODSPEED_U8_COLOR_CODE).dim())
97+
})
98+
.collect();
99+
let tick_strs: Vec<&str> =
100+
tick_strings.iter().map(|s| s.as_str()).collect();
101+
102+
spinner.set_style(
103+
ProgressStyle::with_template(
104+
&format!(
105+
" {{spinner}} {{wide_msg:.{CODSPEED_U8_COLOR_CODE}}} {{elapsed:.dim}}"
106+
),
107+
)
108+
.unwrap()
109+
.tick_strings(&tick_strs),
110+
);
111+
spinner.set_message(format!("{name}..."));
112+
spinner.enable_steady_tick(Duration::from_millis(300));
113+
SPINNER.lock().unwrap().replace(spinner);
114+
} else {
115+
eprintln!("{name}...");
116+
}
107117
}
108118
}
109119
GroupEvent::End => {
@@ -119,7 +129,7 @@ impl Log for LocalLogger {
119129
let elapsed_str = format_elapsed(elapsed);
120130
eprintln!(
121131
"{} {}",
122-
format_checkmark(&name),
132+
format_checkmark(&name, true),
123133
style(elapsed_str).dim(),
124134
);
125135
}
@@ -147,18 +157,19 @@ impl Log for LocalLogger {
147157

148158
/// Format a group header with styled prefix
149159
fn format_group_header(name: &str) -> String {
150-
let prefix = style("\u{25B6}").color256(CODSPEED_U8_COLOR_CODE).bold();
160+
let prefix = style("\u{f0da}").color256(CODSPEED_U8_COLOR_CODE).bold();
151161
let title = style(name).bold();
152162
format!("{prefix} {title}")
153163
}
154164

155-
/// Format a completion checkmark with a dimmed label.
156-
pub(crate) fn format_checkmark(label: &str) -> String {
157-
format!(
158-
" {} {}",
159-
style("\u{2714} ").green().bold(),
160-
style(label).dim(),
161-
)
165+
/// Format a completion checkmark with a label.
166+
pub(crate) fn format_checkmark(label: &str, dim: bool) -> String {
167+
let label = if dim {
168+
style(label).dim().to_string()
169+
} else {
170+
label.to_string()
171+
};
172+
format!(" {} {}", style("\u{f00c}").green().bold(), label)
162173
}
163174

164175
/// Format elapsed duration in a compact human-readable way
@@ -196,13 +207,13 @@ fn indent_lines(s: &str, indent: &str) -> String {
196207
fn print_record(record: &log::Record) {
197208
match record.level() {
198209
log::Level::Error => {
199-
let prefix = style("\u{2717}").red().bold();
210+
let prefix = style("\u{f00d}").red().bold();
200211
let msg = indent_lines(&format!("{}", record.args()), " ");
201212
let msg = Style::new().red().apply_to(msg);
202213
eprintln!(" {prefix} {msg}");
203214
}
204215
log::Level::Warn => {
205-
let prefix = style("\u{25B2}").yellow();
216+
let prefix = style("\u{f071}").yellow();
206217
let msg = indent_lines(&format!("{}", record.args()), " ");
207218
let msg = Style::new().yellow().apply_to(msg);
208219
eprintln!(" {prefix} {msg}");

src/local_logger/rolling_buffer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ impl RollingBuffer {
189189
/// Render the finished frame (checkmark title instead of spinner).
190190
fn render_finished_frame(&self) -> Vec<String> {
191191
let mut frame = Vec::new();
192-
frame.push(format_checkmark(&self.title));
192+
frame.push(format_checkmark(&self.title, false));
193193
frame.push(self.render_top_delimiter());
194194
for line in &self.lines {
195195
frame.push(self.render_content_line(line));

src/upload/poll_results.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ pub async fn poll_results(
5050
let rounded_impact = (impact * 100.0).round();
5151
let (arrow, impact_text) = if impact > 0.0 {
5252
(
53-
style("\u{25B2}").green(),
53+
style("\u{f062}").green(),
5454
style(format!("+{rounded_impact}%")).green().bold(),
5555
)
5656
} else if impact < 0.0 {
5757
(
58-
style("\u{25BC}").red(),
58+
style("\u{f063}").red(),
5959
style(format!("{rounded_impact}%")).red().bold(),
6060
)
6161
} else {
@@ -90,14 +90,14 @@ pub async fn poll_results(
9090
);
9191
} else {
9292
end_group!();
93-
start_group!("Benchmark results");
93+
start_opened_group!("Benchmark results");
9494

9595
if options.detailed_single && response.run.results.len() == 1 {
9696
let summary = build_detailed_summary(&response.run.results[0]);
97-
info!("\n{summary}");
97+
info!("{summary}\n");
9898
} else {
9999
let table = build_benchmark_table(&response.run.results);
100-
info!("\n{table}");
100+
info!("{table}\n");
101101
}
102102

103103
if options.output_json {
@@ -114,7 +114,6 @@ pub async fn poll_results(
114114
style("View full report:").dim(),
115115
style(response.run.url).blue().bold().underlined()
116116
);
117-
end_group!();
118117
}
119118

120119
Ok(())

0 commit comments

Comments
 (0)