1use std::{ffi::OsString, path::PathBuf, sync::Arc};
2
3use ahtml::arc_util::IntoArc;
4use anyhow::{Result, bail};
5use cj_path_util::path_util::AppendToPath;
6use clap::Parser;
7
8use evobench_tools::{
9 ctx,
10 git::GitHash,
11 info,
12 run::{
13 config::{RunConfig, RunConfigBundle},
14 global_app_state_dir::GlobalAppStateDir,
15 output_directory::{
16 html_files::regenerate_index_files,
17 post_process::compress_file_as,
18 structure::{KeyDir, OutputSubdir, RunDir, SubDirs},
19 },
20 working_directory_pool::WorkingDirectoryPoolBaseDir,
21 },
22 serde_types::proper_dirname::ProperDirname,
23 util::grep_diff::GrepDiffRegion,
24 utillib::{
25 get_terminal_width::get_terminal_width,
26 into_arc_path::IntoArcPath,
27 logging::{LogLevelOpts, set_log_level},
28 },
29};
30
31#[derive(clap::Parser, Debug)]
32#[clap(next_line_help = true)]
33#[clap(term_width = get_terminal_width(4))]
34struct Opts {
36 #[clap(flatten)]
37 log_level: LogLevelOpts,
38
39 #[clap(long)]
46 config: Option<PathBuf>,
47
48 #[clap(subcommand)]
51 subcommand: SubCommand,
52}
53
54#[derive(clap::Subcommand, Debug)]
55enum SubCommand {
56 GrepDiff {
61 #[clap(long, short)]
63 commit: Option<GitHash>,
64
65 #[clap(long, short)]
67 target: Option<ProperDirname>,
68
69 #[clap(long, short)]
79 params: Option<String>,
80
81 regex_start: String,
83
84 regex_end: String,
86
87 logfiles: Vec<PathBuf>,
93 },
94
95 PostProcessSingle {
99 #[clap(long)]
102 no_stats: bool,
103
104 run_dir: PathBuf,
107 },
108
109 PostProcessSummary {
113 #[clap(long)]
116 single: bool,
117
118 #[clap(long)]
122 no_single_stats: bool,
123
124 #[clap(long)]
127 no_summary_stats: bool,
128
129 key_dir: PathBuf,
133 },
134
135 Dev {
138 #[clap(subcommand)]
139 subcommand: DevSubCommand,
140 },
141}
142
143#[derive(clap::Subcommand, Debug)]
144enum DevSubCommand {
145 RegenerateIndexFiles,
147
148 ListOutputDir { dir_path: PathBuf },
150}
151
152fn post_process_single(run_dir: &RunDir, run_config: &RunConfig, no_stats: bool) -> Result<()> {
153 let target = run_dir.parent().parent().target_name();
154 let standard_log_path = run_dir.standard_log_path();
155 if !standard_log_path.exists() {
156 info!(
157 "missing {standard_log_path:?} -- try to find and move it \
158 from the working directory pool dir"
159 );
160
161 let date_time_with_offset_str = run_dir.timestamp().as_str();
162
163 let global_app_state_dir = GlobalAppStateDir::new()?;
165 let pool_base_dir = WorkingDirectoryPoolBaseDir::new(
166 run_config.working_directory_pool.base_dir.clone(),
167 &|| global_app_state_dir.working_directory_pool_base(),
168 )?;
169 let pool_base_dir_path = pool_base_dir.path();
170 let found_log_file_name = {
173 let mut file_names: Vec<OsString> = pool_base_dir_path
174 .read_dir()
175 .map_err(ctx!("reading dir {pool_base_dir_path:?}"))?
176 .map(|entry| -> Result<_> {
177 let entry = entry?;
178 let file_name = entry.file_name();
179 if file_name
180 .to_string_lossy()
181 .contains(date_time_with_offset_str)
182 {
183 Ok(Some(file_name))
184 } else {
185 Ok(None)
186 }
187 })
188 .filter_map(|v| v.transpose())
189 .collect::<Result<_>>()?;
190 match file_names.len() {
191 1 => file_names.pop().expect("seen"),
192 0 => bail!(
193 "can't find standard log at {standard_log_path:?} and finding \
194 {date_time_with_offset_str:?} in {pool_base_dir_path:?} was unsuccessful"
195 ),
196 _ => bail!(
197 "got more than one match for {date_time_with_offset_str:?} in \
198 {pool_base_dir_path:?}"
199 ),
200 }
201 };
202
203 let found_log_file_path = pool_base_dir_path.append(&found_log_file_name);
204 info!("found file {found_log_file_path:?}");
205
206 compress_file_as(&found_log_file_path, standard_log_path.clone(), false)?;
207 std::fs::remove_file(&found_log_file_path)?;
208 info!("deleted moved file {found_log_file_path:?}");
209 }
210 run_dir.post_process_single(
211 None,
212 || Ok(()),
213 &target,
214 &standard_log_path,
215 run_config,
216 no_stats,
217 )?;
218 Ok(())
219}
220
221fn main() -> Result<()> {
222 let Opts {
223 config,
224 log_level,
225 subcommand,
226 } = Opts::parse();
227
228 set_log_level(log_level.try_into()?);
229
230 let get_config = {
231 move || -> Result<RunConfigBundle> {
232 let config = config.map(Into::into);
233 Ok(RunConfigBundle::load(
234 config,
235 |msg| bail!("can't load config: {msg}"),
236 GlobalAppStateDir::new()?,
237 )?)
238 }
239 };
240
241 match subcommand {
242 SubCommand::GrepDiff {
243 regex_start,
244 regex_end,
245 logfiles,
246 commit,
247 target,
248 params,
249 } => {
250 let grep_diff_region = GrepDiffRegion::from_strings(®ex_start, ®ex_end)?;
251 grep_diff_region.grep_diff(logfiles, commit, target, params)?;
252 }
253 SubCommand::PostProcessSingle { run_dir, no_stats } => {
254 let run_config_bundle = get_config()?;
255 let conf = &run_config_bundle.shareable.run_config;
256
257 let run_dir = RunDir::try_from(run_dir.into_arc_path())?;
258
259 post_process_single(&run_dir, conf, no_stats)?;
260 }
261 SubCommand::PostProcessSummary {
262 single,
263 key_dir,
264 no_single_stats,
265 no_summary_stats,
266 } => {
267 let run_config_bundle = get_config()?;
268 let conf = &run_config_bundle.shareable.run_config;
269
270 let key_dir: Arc<_> = KeyDir::try_from(key_dir.into_arc_path())?.into();
271
272 if single {
273 for run_dir in key_dir.sub_dirs()? {
274 let run_dir = run_dir?;
275 post_process_single(&run_dir, conf, no_single_stats)?;
276 }
277 }
278
279 key_dir.generate_summaries_for_key_dir(no_summary_stats)?;
280 }
281 SubCommand::Dev { subcommand } => match subcommand {
282 DevSubCommand::RegenerateIndexFiles => {
283 let run_config_bundle = get_config()?;
284 regenerate_index_files(&run_config_bundle.shareable, None, None)?;
285 }
286 DevSubCommand::ListOutputDir { dir_path } => {
287 let dir_path = dir_path.into_arc_path();
288 let output_subdir = OutputSubdir::try_from(dir_path)?;
289 println!(
290 "Listing for {} at {:?}:",
291 output_subdir.type_name(),
292 output_subdir.to_path()
293 );
294 for subdir in output_subdir.into_arc().sub_dirs()? {
295 let subdir = subdir?;
296 println!("{} at {:?}", subdir.type_name(), subdir.to_path());
297 }
298 }
299 },
300 }
301
302 Ok(())
303}