cargo_feature_matrix/
execution.rs1use crate::{features::FeatureMatrix, Error};
2use lazy_static::lazy_static;
3use std::{
4 env, ffi::OsString, io::Write, path::Path, process::Command, process::Stdio,
5};
6use yansi::Paint;
7
8pub(crate) struct Task<'t> {
9 matrix: FeatureMatrix<'t>,
10 manifest_path: Option<&'t Path>,
11 package_name: &'t str,
12 args: &'t [String],
13 command: &'t str,
14 kind: TaskKind,
15}
16
17#[derive(Copy, Clone)]
18pub enum TaskKind {
19 DryRun,
20 PrintJobs,
21 Execute,
22}
23
24impl<'t> Task<'t> {
25 pub(crate) fn new(
26 kind: TaskKind,
27 command: &'t str,
28 manifest_path: Option<&'t Path>,
29 package_name: &'t str,
30 args: &'t [String],
31 matrix: FeatureMatrix<'t>,
32 ) -> Self {
33 Self {
34 matrix,
35 manifest_path,
36 package_name,
37 args,
38 command,
39 kind,
40 }
41 }
42
43 pub(crate) fn run(self) -> Result<(), Error> {
44 match self.kind {
45 TaskKind::DryRun => self.execute(true),
46 TaskKind::PrintJobs => {
47 self.print_jobs();
48 Ok(())
49 }
50 TaskKind::Execute => self.execute(false),
51 }
52 }
53
54 fn execute(self, dry_run: bool) -> Result<(), Error> {
55 let mut exit_status = None;
56 for feature_set in self.matrix {
57 let feature_set = feature_set.to_string();
58
59 let cmd = if dry_run {
60 None
61 } else {
62 let mut cmd = Command::new(CARGO.as_os_str());
63 cmd
64 .arg(self.command)
65 .args(self.args.iter())
66 .stderr(Stdio::piped())
67 .stdout(Stdio::piped())
68 .arg("--no-default-features")
69 .arg("--package")
70 .arg(self.package_name);
71
72 if let Some(manifest_path) = self.manifest_path {
73 cmd.arg("--manifest-path").arg(manifest_path);
74 }
75
76 if !feature_set.is_empty() {
77 cmd.arg("--features").arg(&feature_set);
78 }
79
80 Some(cmd)
81 };
82
83 print!(
84 "{}{} {}{} {}{}{}......",
85 "running: cmd=".cyan(),
86 self.command,
87 "package=".cyan(),
88 self.package_name,
89 "features=[".cyan(),
90 feature_set,
91 "]".cyan(),
92 );
93 std::io::stdout().flush().expect("failed to flush stdout");
94
95 let on_success = || println!("{}", "OK".black().on_green());
97
98 match cmd {
99 None => on_success(),
100 Some(mut cmd) => {
101 let output = cmd.output().map_err(|err| Error::Io {
102 message: "failed to get output of cargo command",
103 source: err,
104 })?;
105 if output.status.success() {
106 on_success();
107 } else {
108 println!("{}", "Fail".black().on_red());
110 exit_status = Some(output.status);
111 std::io::stderr()
112 .write_all(&output.stderr)
113 .expect("failed to write to stderr");
114 std::io::stdout()
115 .write_all(&output.stdout)
116 .expect("failed to write to stdout");
117 }
118 }
119 };
120 }
121
122 match exit_status {
123 Some(exit_status) => Err(Error::Fail(exit_status)),
124 None => Ok(()),
125 }
126 }
127
128 fn print_jobs(self) {
129 let prefix = format!(
130 "{} {} --package {} {}",
131 CARGO.to_string_lossy(),
132 self.command,
133 self.package_name,
134 self.args.join(" ")
135 );
136 for feature_set in self.matrix {
137 if feature_set.is_empty() {
138 println!("{}", prefix);
139 } else {
140 println!("{} --features {}", prefix, feature_set);
141 }
142 }
143 }
144}
145
146lazy_static! {
147 static ref CARGO: OsString =
148 env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
149}