diff options
Diffstat (limited to 'vendor/ui_test/src/cmd.rs')
-rw-r--r-- | vendor/ui_test/src/cmd.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/vendor/ui_test/src/cmd.rs b/vendor/ui_test/src/cmd.rs new file mode 100644 index 000000000..63d055e28 --- /dev/null +++ b/vendor/ui_test/src/cmd.rs @@ -0,0 +1,120 @@ +use std::{ + ffi::OsString, + path::{Path, PathBuf}, + process::Command, +}; + +#[derive(Debug, Clone)] +/// A command, its args and its environment. Used for +/// the main command, the dependency builder and the cfg-reader. +pub struct CommandBuilder { + /// Path to the binary. + pub program: PathBuf, + /// Arguments to the binary. + pub args: Vec<OsString>, + /// A flag to prefix before the path to where output files should be written. + pub out_dir_flag: Option<OsString>, + /// A flag to set as the last flag in the command, so the `build` caller can + /// append the filename themselves. + pub input_file_flag: Option<OsString>, + /// Environment variables passed to the binary that is executed. + /// The environment variable is removed if the second tuple field is `None` + pub envs: Vec<(OsString, Option<OsString>)>, +} + +impl CommandBuilder { + /// Uses the `CARGO` env var or just a program named `cargo` and the argument `build`. + pub fn cargo() -> Self { + Self { + program: PathBuf::from(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into())), + args: vec!["build".into()], + out_dir_flag: Some("--target-dir".into()), + input_file_flag: Some("--manifest-path".into()), + envs: vec![], + } + } + + /// Uses the `RUSTC` env var or just a program named `rustc` and the argument `--error-format=json`. + /// + /// Take care to only append unless you actually meant to overwrite the defaults. + /// Overwriting the defaults may make `//~ ERROR` style comments stop working. + pub fn rustc() -> Self { + Self { + program: PathBuf::from(std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into())), + args: vec!["--error-format=json".into()], + out_dir_flag: Some("--out-dir".into()), + input_file_flag: None, + envs: vec![], + } + } + + /// Same as [`rustc`], but with arguments for obtaining the cfgs. + pub fn cfgs() -> Self { + Self { + args: vec!["--print".into(), "cfg".into()], + ..Self::rustc() + } + } + + /// Build a `CommandBuilder` for a command without any argumemnts. + /// You can still add arguments later. + pub fn cmd(cmd: impl Into<PathBuf>) -> Self { + Self { + program: cmd.into(), + args: vec![], + out_dir_flag: None, + input_file_flag: None, + envs: vec![], + } + } + + /// Render the command like you'd use it on a command line. + pub fn display(&self) -> impl std::fmt::Display + '_ { + struct Display<'a>(&'a CommandBuilder); + impl std::fmt::Display for Display<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (var, val) in &self.0.envs { + if let Some(val) = val { + write!(f, "{var:?}={val:?} ")?; + } + } + self.0.program.display().fmt(f)?; + for arg in &self.0.args { + write!(f, " {arg:?}")?; + } + if let Some(flag) = &self.0.out_dir_flag { + write!(f, " {flag:?} OUT_DIR")?; + } + if let Some(flag) = &self.0.input_file_flag { + write!(f, " {flag:?}")?; + } + Ok(()) + } + } + Display(self) + } + + /// Create a command with the given settings. + pub fn build(&self, out_dir: &Path) -> Command { + let mut cmd = Command::new(&self.program); + cmd.args(self.args.iter()); + if let Some(flag) = &self.out_dir_flag { + cmd.arg(flag).arg(out_dir); + } + if let Some(flag) = &self.input_file_flag { + cmd.arg(flag); + } + self.apply_env(&mut cmd); + cmd + } + + pub(crate) fn apply_env(&self, cmd: &mut Command) { + for (var, val) in self.envs.iter() { + if let Some(val) = val { + cmd.env(var, val); + } else { + cmd.env_remove(var); + } + } + } +} |