xtask/
command.rs

1use std::{
2    path::PathBuf,
3    process::{self, Stdio},
4};
5
6pub struct Command {
7    bin: String,
8    args: Vec<String>,
9    error_message: String,
10    dry_run: bool,
11    current_dir: Option<PathBuf>,
12}
13
14impl Command {
15    pub fn program<S: AsRef<str>>(bin: S) -> Self {
16        Self {
17            bin: bin.as_ref().to_owned(),
18            args: vec![],
19            error_message: String::new(),
20            dry_run: false,
21            current_dir: None,
22        }
23    }
24
25    pub fn args<S: AsRef<str>, V: AsRef<[S]>>(mut self, args: V) -> Self {
26        self.args
27            .extend(args.as_ref().iter().map(|s| s.as_ref().to_string()));
28        self
29    }
30
31    pub fn error_message<S: AsRef<str>>(mut self, message: S) -> Self {
32        message.as_ref().clone_into(&mut self.error_message);
33        self
34    }
35
36    pub fn dry_run(mut self, dry_run: bool) -> Self {
37        self.dry_run = dry_run;
38        self
39    }
40
41    pub fn current_dir(mut self, current_dir: PathBuf) -> Self {
42        self.current_dir = Some(current_dir);
43        self
44    }
45
46    pub fn execute(self) {
47        let mut cmd = process::Command::new(self.bin);
48        cmd.args(&self.args)
49            .stderr(Stdio::inherit())
50            .stdout(Stdio::inherit());
51        if let Some(current_dir) = self.current_dir {
52            cmd.current_dir(&current_dir);
53        }
54        if self.dry_run {
55            println!("{cmd:?}");
56            return;
57        }
58        let status = cmd.status();
59        assert!(
60            {
61                if self.error_message.is_empty() {
62                    status.unwrap()
63                } else {
64                    status.expect(&self.error_message)
65                }
66            }
67            .success()
68        );
69    }
70
71    pub fn output_string(self) -> String {
72        let mut cmd = process::Command::new(self.bin);
73        cmd.args(&self.args).stderr(Stdio::inherit());
74        if let Some(current_dir) = self.current_dir {
75            cmd.current_dir(&current_dir);
76        }
77        let output = cmd.output();
78        String::from_utf8({
79            if self.error_message.is_empty() {
80                output.unwrap().stdout
81            } else {
82                output.expect(&self.error_message).stdout
83            }
84        })
85        .expect("Stdout contains non UTF-8 characters")
86    }
87}