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