devbox_build/
cmd.rs

1use std::collections::HashMap;
2use std::ffi::{OsStr, OsString};
3use std::io::Result;
4use std::path::PathBuf;
5use std::process::{Command, ExitStatus, Output};
6
7//-- Cmd -------------------------------------------------------------------------------------------
8
9/// Clone-able variant of [`std::process::Command`] with some build specific helper methods
10///
11/// [`std::process::Command`]: (https://doc.rust-lang.org/std/process/struct.Command.html)
12///
13/// Cloning allows configuring commands with some common arguments and then invoking it with
14/// additional arguments/env vars at different places inside the script.
15///
16/// Method of this type are mirroring those of std::process::Command with addition of [`run`] and
17/// [`run_result`] commands for easier use inside of build sript.
18///
19/// [`run`]: #method.run
20/// [`run_result`]: #method.run_result
21///
22#[derive(Clone, Debug)]
23pub struct Cmd {
24    program: OsString,
25    args: Vec<OsString>,
26    envs: HashMap<OsString, OsString>,
27    work: Option<PathBuf>,
28}
29
30impl Cmd {
31
32    /// Constructs a new Cmd for launching the executable at path `program`
33    pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
34        Self {
35            program: program.as_ref().to_owned(),
36            args: vec![],
37            envs: HashMap::new(),
38            work: None,
39        }
40    }
41
42    /// Adds an argument to the list of execution arguments
43    pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
44        self.args.push(arg.as_ref().to_owned());
45        self
46    }
47
48    /// Adds multiple arguments to the list of execution arguments
49    pub fn args<I, S>(mut self, args: I) -> Self
50        where
51            I: IntoIterator<Item = S>,
52            S: AsRef<OsStr>,
53    {
54        self.args.extend(args.into_iter().map(|e| e.as_ref().to_owned()));
55        self
56    }
57
58    /// Sets an environment variable
59    pub fn env<K: AsRef<OsStr>, V: AsRef<OsStr>>(mut self, env: K, val: V) -> Self {
60        self.envs.insert(env.as_ref().to_owned(), val.as_ref().to_owned());
61        self
62    }
63
64    /// Run the command and return it's output.
65    ///
66    /// This is convienece method for calling [`std::process::Command::output()`] method on command
67    /// instance retrieved by [`command`] method
68    ///
69    /// [`command`]: #method.command
70    /// [`std::process::Command::output()`]:
71    /// https://doc.rust-lang.org/std/process/struct.Command.html#method.output
72    pub fn output(&self) -> Output {
73        println!("Executing: {:?} {:?} {:?}", self.program, self.args, self.envs);
74        self.command().output().expect(format!("Command executon '{:?} {:?} {:?}' failed",
75            self.program, self.args, self.envs).as_str()
76        )
77    }
78
79    /// Run the command and exit the build with informative panic message if execution fails.
80    ///
81    /// This is convienece method for calling [`std::process::Command::status()`] method on command
82    /// instance retrieved by [`command`] method
83    ///
84    /// [`command`]: #method.command
85    /// [`std::process::Command::status()`]:
86    /// https://doc.rust-lang.org/std/process/struct.Command.html#method.status
87    pub fn run(&self) {
88        println!("Executing: {:?} {:?} {:?}", self.program, self.args, self.envs);
89        assert!(self.run_result().expect(format!("Command executon '{:?} {:?} {:?}' failed",
90            self.program, self.args, self.envs).as_str()
91        ).success());
92    }
93
94    /// Run the command and return it's status.
95    ///
96    /// This is convienece method for calling [`std::process::Command::status()`] method on command
97    /// instance retrieved by [`command`] method
98    ///
99    /// [`command`]: #method.command
100    /// [`std::process::Command::status()`]:
101    /// https://doc.rust-lang.org/std/process/struct.Command.html#method.status
102    pub fn run_result(&self) -> Result<ExitStatus> {
103        self.command().status()
104    }
105
106    /// Build the `std::process::Command` with args and environment variables set up by methods on
107    /// this Cmd instance.
108    pub fn command(&self) -> Command {
109        let mut command = Command::new(&self.program);
110        command.args(&self.args);
111        command.envs(&self.envs);
112
113        if let Some(work_dir) = &self.work {
114            command.current_dir(work_dir);
115        }
116
117        command
118    }
119}