#![cfg_attr(feature = "build-metrics", allow(unused))] use std::str::FromStr; use std::path::PathBuf; use clap::Parser; use crate::{builder::Builder, tool::Tool}; /// Suggests a list of possible `x.py` commands to run based on modified files in branch. pub fn suggest(builder: &Builder<'_>, run: bool) { let suggestions = builder.tool_cmd(Tool::SuggestTests).output().expect("failed to run `suggest-tests` tool"); if !suggestions.status.success() { println!("failed to run `suggest-tests` tool ({})", suggestions.status); println!( "`suggest_tests` stdout:\n{}`suggest_tests` stderr:\n{}", String::from_utf8(suggestions.stdout).unwrap(), String::from_utf8(suggestions.stderr).unwrap() ); panic!("failed to run `suggest-tests`"); } let suggestions = String::from_utf8(suggestions.stdout).unwrap(); let suggestions = suggestions .lines() .map(|line| { let mut sections = line.split_ascii_whitespace(); // this code expects one suggestion per line in the following format: // {some number of flags} [optional stage number] let cmd = sections.next().unwrap(); let stage = sections.next_back().map(|s| str::parse(s).ok()).flatten(); let paths: Vec = sections.map(|p| PathBuf::from_str(p).unwrap()).collect(); (cmd, stage, paths) }) .collect::>(); if !suggestions.is_empty() { println!("==== SUGGESTIONS ===="); for sug in &suggestions { print!("x {} ", sug.0); if let Some(stage) = sug.1 { print!("--stage {stage} "); } for path in &sug.2 { print!("{} ", path.display()); } println!(); } println!("====================="); } else { println!("No suggestions found!"); return; } if run { for sug in suggestions { let mut build: crate::Build = builder.build.clone(); build.config.paths = sug.2; build.config.cmd = crate::flags::Flags::parse_from(["x.py", sug.0]).cmd; if let Some(stage) = sug.1 { build.config.stage = stage; } build.build(); } } else { println!("help: consider using the `--run` flag to automatically run suggested tests"); } }