git_perf/
cli.rs

1use anyhow::Result;
2use clap::CommandFactory;
3use clap::{error::ErrorKind::ArgumentConflict, Parser};
4use env_logger::Env;
5use log::Level;
6
7use crate::audit;
8use crate::basic_measure::measure;
9use crate::config::bump_epoch;
10use crate::git::git_interop::check_git_version;
11use crate::git::git_interop::{list_commits_with_measurements, prune, pull, push};
12use crate::measurement_storage::{add, remove_measurements_from_commits};
13use crate::reporting::report;
14use crate::stats::ReductionFunc;
15use git_perf_cli_types::{Cli, Commands};
16
17pub fn handle_calls() -> Result<()> {
18    let cli = Cli::parse();
19    let logger_level = match cli.verbose {
20        0 => Level::Warn,
21        1 => Level::Info,
22        2 => Level::Debug,
23        _ => Level::Trace,
24    };
25    env_logger::Builder::from_env(Env::default().default_filter_or(logger_level.as_str())).init();
26
27    check_git_version()?;
28
29    match cli.command {
30        Commands::Measure {
31            repetitions,
32            command,
33            measurement,
34        } => measure(
35            &measurement.name,
36            repetitions,
37            &command,
38            &measurement.key_value,
39        ),
40        Commands::Add { value, measurement } => {
41            add(&measurement.name, value, &measurement.key_value)
42        }
43        Commands::Push { remote } => push(None, remote.as_deref()),
44        Commands::Pull {} => pull(None),
45        Commands::Report {
46            output,
47            separate_by,
48            report_history,
49            measurement,
50            key_value,
51            aggregate_by,
52        } => report(
53            output,
54            separate_by,
55            report_history.max_count,
56            &measurement,
57            &key_value,
58            aggregate_by.map(ReductionFunc::from),
59        ),
60        Commands::Audit {
61            measurement,
62            report_history,
63            selectors,
64            min_measurements,
65            aggregate_by,
66            sigma,
67            dispersion_method,
68        } => {
69            if report_history.max_count < min_measurements.into() {
70                Cli::command().error(ArgumentConflict, format!("The minimal number of measurements ({}) cannot be more than the maximum number of measurements ({})", min_measurements, report_history.max_count)).exit()
71            }
72
73            let final_dispersion_method =
74                determine_dispersion_method(dispersion_method, &measurement);
75
76            audit::audit_multiple(
77                &measurement,
78                report_history.max_count,
79                min_measurements,
80                &selectors,
81                ReductionFunc::from(aggregate_by),
82                sigma,
83                final_dispersion_method,
84            )
85        }
86        Commands::BumpEpoch { measurement } => bump_epoch(&measurement),
87        Commands::Prune {} => prune(),
88        Commands::Remove {
89            older_than,
90            no_prune,
91        } => remove_measurements_from_commits(older_than, !no_prune),
92        Commands::ListCommits {} => {
93            let commits = list_commits_with_measurements()?;
94            for commit in commits {
95                println!("{}", commit);
96            }
97            Ok(())
98        }
99    }
100}
101
102/// Determine the final dispersion method with proper precedence:
103/// 1. CLI option (if specified)
104/// 2. Configuration file (measurement-specific or global)
105/// 3. Default (stddev)
106fn determine_dispersion_method(
107    cli_method: Option<git_perf_cli_types::DispersionMethod>,
108    measurement: &[String],
109) -> crate::stats::DispersionMethod {
110    if let Some(cli_method) = cli_method {
111        // User explicitly specified a dispersion method via CLI
112        crate::stats::DispersionMethod::from(cli_method)
113    } else {
114        // User didn't specify --dispersion-method, try to get from configuration
115        if measurement.is_empty() {
116            crate::stats::DispersionMethod::StandardDeviation
117        } else {
118            // Use configuration for the first measurement, or fall back to StandardDeviation
119            let config_method = crate::config::audit_dispersion_method(&measurement[0]);
120            crate::stats::DispersionMethod::from(config_method)
121        }
122    }
123}