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, resolve_key_values};
10use crate::config_cmd;
11use crate::git::git_interop::check_git_version;
12use crate::git::git_interop::{list_commits_with_measurements, prune, pull, push};
13use crate::import::{handle_import, ImportOptions};
14use crate::measurement_storage::{add_to_commit as add, remove_measurements_from_commits};
15use crate::reporting::report;
16use crate::reset;
17use crate::size;
18use crate::stats::ReductionFunc;
19use crate::status;
20use crate::study;
21use git_perf_cli_types::{Cli, Commands};
22
23pub fn handle_calls() -> Result<()> {
24 let cli = Cli::parse();
25 let logger_level = match cli.verbose {
26 0 => Level::Warn,
27 1 => Level::Info,
28 2 => Level::Debug,
29 _ => Level::Trace,
30 };
31 env_logger::Builder::from_env(Env::default().default_filter_or(logger_level.as_str())).init();
32
33 check_git_version()?;
34
35 match cli.command {
36 Commands::Measure {
37 repetitions,
38 measurement,
39 commit,
40 skip_env,
41 command,
42 } => {
43 let commit = commit.as_deref().unwrap_or("HEAD");
44 let key_values = resolve_key_values(&measurement.key_value, skip_env);
45 measure(
46 commit,
47 &measurement.name,
48 repetitions,
49 &command,
50 &key_values,
51 )
52 }
53 Commands::Add {
54 value,
55 measurement,
56 commit,
57 skip_env,
58 } => {
59 let commit = commit.as_deref().unwrap_or("HEAD");
60 let key_values = resolve_key_values(&measurement.key_value, skip_env);
61 add(commit, &measurement.name, value, &key_values)
62 }
63 Commands::Import {
64 format,
65 file,
66 commit,
67 prefix,
68 metadata,
69 filter,
70 dry_run,
71 verbose,
72 skip_env,
73 } => {
74 let commit = commit.as_deref().unwrap_or("HEAD").to_string();
75 let metadata = resolve_key_values(&metadata, skip_env);
76 handle_import(ImportOptions {
77 commit,
78 format,
79 file,
80 prefix,
81 metadata,
82 filter,
83 dry_run,
84 verbose,
85 })
86 }
87 Commands::Push { remote } => push(None, remote.as_deref()),
88 Commands::Pull {} => pull(None),
89 Commands::Report {
90 commit,
91 output,
92 separate_by,
93 report_history,
94 measurement,
95 key_value,
96 aggregate_by,
97 filter,
98 template,
99 custom_css,
100 title,
101 show_epochs,
102 show_changes,
103 } => {
104 let commit = commit.as_deref().unwrap_or("HEAD");
105
106 let combined_patterns =
108 crate::filter::combine_measurements_and_filters(&measurement, &filter);
109
110 let template_config = crate::reporting::ReportTemplateConfig {
111 template_path: template,
112 custom_css_path: custom_css,
113 title,
114 };
115
116 report(
117 commit,
118 output,
119 separate_by,
120 report_history.max_count,
121 report_history.since.as_deref(),
122 report_history.until.as_deref(),
123 &key_value,
124 aggregate_by.map(ReductionFunc::from),
125 &combined_patterns,
126 template_config,
127 show_epochs,
128 show_changes,
129 )
130 }
131 Commands::Audit {
132 commit,
133 measurement,
134 report_history,
135 selectors,
136 separate_by,
137 min_measurements,
138 aggregate_by,
139 sigma,
140 dispersion_method,
141 max_cov,
142 filter,
143 no_change_point_warning,
144 } => {
145 let commit = commit.as_deref().unwrap_or("HEAD");
146 if measurement.is_empty() && filter.is_empty() {
149 Cli::command()
150 .error(
151 clap::error::ErrorKind::MissingRequiredArgument,
152 "At least one of --measurement or --filter must be provided",
153 )
154 .exit()
155 }
156
157 if let Some(min_count) = min_measurements {
159 if report_history.max_count < min_count.into() {
160 Cli::command().error(ArgumentConflict, format!("The minimal number of measurements ({}) cannot be more than the maximum number of measurements ({})", min_count, report_history.max_count)).exit()
161 }
162 }
163
164 let combined_patterns =
166 crate::filter::combine_measurements_and_filters(&measurement, &filter);
167
168 audit::audit_multiple(
169 commit,
170 report_history.max_count,
171 report_history.since.as_deref(),
172 report_history.until.as_deref(),
173 min_measurements,
174 &selectors,
175 aggregate_by.map(ReductionFunc::from),
176 sigma,
177 dispersion_method.map(crate::stats::DispersionMethod::from),
178 max_cov,
179 &combined_patterns,
180 &separate_by,
181 no_change_point_warning,
182 )
183 }
184 Commands::BumpEpoch { measurements } => {
185 for measurement in measurements {
186 bump_epoch(&measurement)?;
187 }
188 Ok(())
189 }
190 Commands::Prune {} => prune(),
191 Commands::Status { detailed } => status::show_status(detailed),
192 Commands::Reset { dry_run, force } => reset::reset_measurements(dry_run, force),
193 Commands::Remove {
194 older_than,
195 no_prune,
196 dry_run,
197 } => remove_measurements_from_commits(older_than, !no_prune, dry_run),
198 Commands::ListCommits {} => {
199 let commits = list_commits_with_measurements()?;
200 for commit in commits {
201 println!("{}", commit);
202 }
203 Ok(())
204 }
205 Commands::Size {
206 detailed,
207 format,
208 disk_size,
209 include_objects,
210 } => size::calculate_measurement_size(detailed, format, disk_size, include_objects),
211 Commands::Study {
212 name,
213 max_count,
214 max_cov,
215 commit,
216 group_by,
217 } => {
218 let commit = commit.as_deref().unwrap_or("HEAD");
219 study::run_study(commit, max_count, &name, max_cov, &group_by)
220 }
221 Commands::Config {
222 list,
223 detailed,
224 format,
225 validate,
226 measurement,
227 } => {
228 if list {
229 config_cmd::list_config(detailed, format, validate, measurement)
230 } else {
231 anyhow::bail!("config command requires --list flag (try: git perf config --list)");
234 }
235 }
236 }
237}