use std::collections::BTreeMap;
use std::fmt;
use super::super::CTPROF_DERIVED_METRICS;
use super::super::columns::{Column, Section};
use super::super::diff_types::{CtprofDiff, DerivedRow};
use super::super::options::GroupBy;
use super::super::render::{color_diff_cell, colored_header_with_sort, render_derived_row_cells};
use super::super::runner::DisplayOptions;
use super::primary::{build_primary_hier, emit_cgroup_segments, emit_pcomm_heading};
pub(super) fn write_derived_section<W: fmt::Write>(
w: &mut W,
diff: &CtprofDiff,
group_by: GroupBy,
group_header: &'static str,
columns: &[Column],
display: &DisplayOptions,
global_max_widths: &[u16],
) -> fmt::Result {
if !(display.is_section_enabled(Section::Derived)
|| display.is_section_enabled(Section::TaskstatsDelay))
|| diff.derived_rows.is_empty()
{
return Ok(());
}
let derived_rows: Vec<&DerivedRow> = diff
.derived_rows
.iter()
.filter(|row| {
if !display.is_metric_enabled(row.metric_name) {
return false;
}
let metric = CTPROF_DERIVED_METRICS
.iter()
.find(|d| d.name == row.metric_name)
.expect("derived metric_name from CTPROF_DERIVED_METRICS");
display.is_section_enabled(metric.section)
})
.collect();
let uptime_map: BTreeMap<&str, Option<f64>> = diff
.rows
.iter()
.map(|r| (r.group_key.as_str(), r.uptime_pct))
.collect();
if group_by == GroupBy::All {
write_derived_all(
w,
&derived_rows,
columns,
display,
global_max_widths,
&uptime_map,
diff.sort_metric_name,
)?;
} else {
write_derived_flat(
w,
&derived_rows,
columns,
display,
group_header,
&uptime_map,
diff.sort_metric_name,
)?;
}
Ok(())
}
fn write_derived_all<W: fmt::Write>(
w: &mut W,
derived_rows: &[&DerivedRow],
columns: &[Column],
display: &DisplayOptions,
global_max_widths: &[u16],
uptime_map: &BTreeMap<&str, Option<f64>>,
sort_metric_name: Option<&'static str>,
) -> fmt::Result {
let (fudged_rows, normal_rows): (Vec<&DerivedRow>, Vec<&DerivedRow>) = derived_rows
.iter()
.copied()
.partition(|r| r.display_key.starts_with("[fudged"));
let limited: Vec<&DerivedRow> = if display.section_line_limit > 0 {
let mut out: Vec<&DerivedRow> = fudged_rows.clone();
let budget = display.section_line_limit.saturating_sub(fudged_rows.len());
out.extend(normal_rows.into_iter().take(budget));
out
} else {
let mut out = fudged_rows;
out.extend(normal_rows);
out
};
let hier = build_primary_hier(limited.iter().copied(), |row: &DerivedRow| {
row.group_key.as_str()
});
writeln!(w)?;
writeln!(w, "## Derived metrics")?;
let mut dt = display.new_constrained_table(global_max_widths);
dt.set_header(colored_header_with_sort(columns, "comm", sort_metric_name));
let mut last_segs: Vec<&str> = Vec::new();
let mut last_pc = "";
for h in &hier {
if let Some(new_segs) = emit_cgroup_segments(&mut dt, h.cgroup, &last_segs, columns) {
last_segs = new_segs;
last_pc = "";
}
if h.pcomm != last_pc {
emit_pcomm_heading(&mut dt, h.pcomm, last_segs.len(), columns);
last_pc = h.pcomm;
}
let mut cells = render_derived_row_cells(h.row, columns);
if let Some(pos) = columns.iter().position(|c| *c == Column::Group) {
let cg_depth = last_segs.len();
cells[pos] = format!("{} {}", " ".repeat(cg_depth + 1), h.comm);
}
let colored: Vec<comfy_table::Cell> = cells
.into_iter()
.zip(columns.iter())
.map(|(s, col)| {
let up = uptime_map.get(h.row.group_key.as_str()).copied().flatten();
if *col == Column::Uptime {
let text = match up {
Some(pct) => format!("{pct:.0}%"),
None => "-".to_string(),
};
color_diff_cell(
text,
*col,
h.row.delta,
h.row.delta_pct,
up,
h.row.sort_by_delta,
)
} else {
color_diff_cell(
s,
*col,
h.row.delta,
h.row.delta_pct,
up,
h.row.sort_by_delta,
)
}
})
.collect();
dt.add_row(colored);
}
writeln!(w, "{dt}")?;
Ok(())
}
fn write_derived_flat<W: fmt::Write>(
w: &mut W,
derived_rows: &[&DerivedRow],
columns: &[Column],
display: &DisplayOptions,
group_header: &'static str,
uptime_map: &BTreeMap<&str, Option<f64>>,
sort_metric_name: Option<&'static str>,
) -> fmt::Result {
writeln!(w)?;
writeln!(w, "## Derived metrics")?;
let mut dt = display.new_table();
dt.set_header(colored_header_with_sort(
columns,
group_header,
sort_metric_name,
));
let d_limit = if display.section_line_limit > 0 {
&derived_rows[..derived_rows.len().min(display.section_line_limit)]
} else {
derived_rows
};
for row in d_limit {
let string_cells = render_derived_row_cells(row, columns);
let cells: Vec<comfy_table::Cell> = string_cells
.into_iter()
.zip(columns.iter())
.map(|(s, col)| {
let up = uptime_map.get(row.group_key.as_str()).copied().flatten();
if *col == Column::Uptime {
let text = match up {
Some(pct) => format!("{pct:.0}%"),
None => "-".to_string(),
};
color_diff_cell(text, *col, row.delta, row.delta_pct, up, row.sort_by_delta)
} else {
color_diff_cell(s, *col, row.delta, row.delta_pct, up, row.sort_by_delta)
}
})
.collect();
dt.add_row(cells);
}
writeln!(w, "{dt}")?;
Ok(())
}