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}