Skip to main content

cargo_feature_matrix/
execution.rs

1use 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!("{}", Black.style().bg(Green).paint("OK"));
101      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!("{}", Black.style().bg(Red).paint("Fail"));
114            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}