use crate::cli::StatsCommand;
use ktstr::cli;
pub(crate) fn run_stats(command: &Option<StatsCommand>) -> Result<(), String> {
match command {
None => {
if let Some(output) = cli::print_stats_report() {
print!("{output}");
}
Ok(())
}
Some(StatsCommand::List) => cli::list_runs().map_err(|e| format!("{e:#}")),
Some(StatsCommand::ListMetrics { json }) => match cli::list_metrics(*json) {
Ok(s) => {
print!("{s}");
Ok(())
}
Err(e) => Err(format!("{e:#}")),
},
Some(StatsCommand::ListValues { json, dir }) => {
match cli::list_values(*json, dir.as_deref()) {
Ok(s) => {
print!("{s}");
Ok(())
}
Err(e) => Err(format!("{e:#}")),
}
}
Some(StatsCommand::ShowHost { run, dir }) => {
match cli::show_run_host(run, dir.as_deref()) {
Ok(s) => {
print!("{s}");
Ok(())
}
Err(e) => Err(format!("{e:#}")),
}
}
Some(StatsCommand::ExplainSidecar { run, dir, json }) => {
match cli::explain_sidecar(run, dir.as_deref(), *json) {
Ok(s) => {
print!("{s}");
Ok(())
}
Err(e) => Err(format!("{e:#}")),
}
}
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct BuildCompareFilters {
pub(crate) shared_kernel: Vec<String>,
pub(crate) shared_project_commit: Vec<String>,
pub(crate) shared_kernel_commit: Vec<String>,
pub(crate) shared_run_source: Vec<String>,
pub(crate) shared_resolve_source: Vec<String>,
pub(crate) shared_cpu_budget: Vec<String>,
pub(crate) shared_scheduler: Vec<String>,
pub(crate) shared_topology: Vec<String>,
pub(crate) shared_work_type: Vec<String>,
pub(crate) a_kernel: Vec<String>,
pub(crate) a_project_commit: Vec<String>,
pub(crate) a_kernel_commit: Vec<String>,
pub(crate) b_kernel: Vec<String>,
pub(crate) b_project_commit: Vec<String>,
pub(crate) b_kernel_commit: Vec<String>,
}
impl BuildCompareFilters {
pub(crate) fn build(&self) -> (ktstr::cli::RowFilter, ktstr::cli::RowFilter) {
let pick_vec = |a: &[String], shared: &[String]| -> Vec<String> {
if a.is_empty() {
shared.to_vec()
} else {
a.to_vec()
}
};
let filter_a = ktstr::cli::RowFilter {
kernels: pick_vec(&self.a_kernel, &self.shared_kernel),
project_commits: pick_vec(&self.a_project_commit, &self.shared_project_commit),
kernel_commits: pick_vec(&self.a_kernel_commit, &self.shared_kernel_commit),
run_sources: self.shared_run_source.clone(),
resolve_sources: self.shared_resolve_source.clone(),
cpu_budgets: self.shared_cpu_budget.clone(),
schedulers: self.shared_scheduler.clone(),
topologies: self.shared_topology.clone(),
work_types: self.shared_work_type.clone(),
};
let filter_b = ktstr::cli::RowFilter {
kernels: pick_vec(&self.b_kernel, &self.shared_kernel),
project_commits: pick_vec(&self.b_project_commit, &self.shared_project_commit),
kernel_commits: pick_vec(&self.b_kernel_commit, &self.shared_kernel_commit),
run_sources: self.shared_run_source.clone(),
resolve_sources: self.shared_resolve_source.clone(),
cpu_budgets: self.shared_cpu_budget.clone(),
schedulers: self.shared_scheduler.clone(),
topologies: self.shared_topology.clone(),
work_types: self.shared_work_type.clone(),
};
(filter_a, filter_b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_compare_filters_empty_yields_default_default() {
let b = BuildCompareFilters::default();
let (fa, fb) = b.build();
assert!(fa.kernels.is_empty());
assert!(fa.project_commits.is_empty());
assert!(fa.kernel_commits.is_empty());
assert!(fa.run_sources.is_empty());
assert!(fa.schedulers.is_empty());
assert!(fa.topologies.is_empty());
assert!(fa.work_types.is_empty());
assert_eq!(fa.kernels, fb.kernels);
assert_eq!(fa.project_commits, fb.project_commits);
assert_eq!(fa.kernel_commits, fb.kernel_commits);
assert_eq!(fa.run_sources, fb.run_sources);
assert_eq!(fa.schedulers, fb.schedulers);
assert_eq!(fa.topologies, fb.topologies);
assert_eq!(fa.work_types, fb.work_types);
}
#[test]
fn build_compare_filters_per_side_kernel_commit_overrides_shared() {
let b = BuildCompareFilters {
shared_kernel_commit: vec!["abcdef1".to_string(), "fedcba2".to_string()],
a_kernel_commit: vec!["111aaaa".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(
fa.kernel_commits,
vec!["111aaaa"],
"A overrides shared kernel-commit",
);
assert_eq!(
fb.kernel_commits,
vec!["abcdef1", "fedcba2"],
"B retains shared kernel-commit default",
);
}
#[test]
fn build_compare_filters_disjoint_per_side_kernel_commit_slices() {
let b = BuildCompareFilters {
a_kernel_commit: vec!["abcdef1".to_string()],
b_kernel_commit: vec!["fedcba2".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(fa.kernel_commits, vec!["abcdef1"]);
assert_eq!(fb.kernel_commits, vec!["fedcba2"]);
let slicing = ktstr::cli::derive_slicing_dims(&fa, &fb);
assert_eq!(
slicing,
vec![ktstr::cli::Dimension::KernelCommit],
"differing per-side kernel-commit must derive as a single \
KernelCommit slicing dim",
);
}
#[test]
fn build_compare_filters_shared_kernel_pins_both_sides() {
let b = BuildCompareFilters {
shared_kernel: vec!["6.14".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(fa.kernels, vec!["6.14"]);
assert_eq!(fb.kernels, vec!["6.14"]);
}
#[test]
fn build_compare_filters_per_side_overrides_shared_for_that_side_only() {
let b = BuildCompareFilters {
shared_kernel: vec!["6.14".to_string(), "6.15".to_string()],
a_kernel: vec!["6.13".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(fa.kernels, vec!["6.13"], "A overrides shared");
assert_eq!(fb.kernels, vec!["6.14", "6.15"], "B retains shared default",);
}
#[test]
fn build_compare_filters_disjoint_per_side_kernel_yields_two_filters() {
let b = BuildCompareFilters {
a_kernel: vec!["6.14".to_string()],
b_kernel: vec!["6.15".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(fa.kernels, vec!["6.14"]);
assert_eq!(fb.kernels, vec!["6.15"]);
}
#[test]
fn build_compare_filters_empty_run_sources_field_equal_on_both_sides() {
let b = BuildCompareFilters::default();
let (fa, fb) = b.build();
assert!(
fa.run_sources.is_empty(),
"empty BuildCompareFilters must produce A-side filter with empty run_sources",
);
assert!(
fb.run_sources.is_empty(),
"empty BuildCompareFilters must produce B-side filter with empty run_sources",
);
assert_eq!(
fa.run_sources, fb.run_sources,
"both sides must agree on empty run_sources",
);
}
#[test]
fn build_compare_filters_shared_source_pins_both_sides() {
let b = BuildCompareFilters {
shared_run_source: vec!["ci".to_string()],
..BuildCompareFilters::default()
};
let (fa, fb) = b.build();
assert_eq!(fa.run_sources, vec!["ci".to_string()]);
assert_eq!(fb.run_sources, vec!["ci".to_string()]);
}
}