1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use std::{ffi::OsString, fmt};
use serde::{Deserialize, Serialize};
use tokio::process::Command;
/// Shell command to execute.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ShCmd {
/// Command to run.
program: OsString,
/// Arguments to pass to the command.
args: Vec<OsString>,
}
impl ShCmd {
/// Constructs a new `ShCmd` for launching the program at
/// path `program`, with the following default configuration:
///
/// * No arguments to the program
/// * Inherit the current process's environment
/// * Inherit the current process's working directory
/// * Inherit stdin/stdout/stderr for [`spawn`] or [`status`], but create
/// pipes for [`output`]
///
/// [`spawn`]: Self::spawn
/// [`status`]: Self::status
/// [`output`]: Self::output
///
/// Builder methods are provided to change these defaults and
/// otherwise configure the process.
///
/// If `program` is not an absolute path, the `PATH` will be searched in
/// an OS-defined way.
///
/// The search path to be used may be controlled by setting the
/// `PATH` environment variable on the ShCmd,
/// but this has some implementation limitations on Windows
/// (see issue #37519).
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// use peace_item_spec_sh_cmd::ShCmd;
///
/// let sh_cmd = ShCmd::new("sh");
/// ```
pub fn new<S: Into<OsString>>(program: S) -> Self {
Self {
program: program.into(),
args: Vec::new(),
}
}
/// Adds an argument to pass to the program.
///
/// Only one argument can be passed per use. So instead of:
///
/// ```rust,no_run
/// # peace_item_spec_sh_cmd::ShCmd::new("sh")
/// .arg("-C /path/to/repo")
/// # ;
/// ```
///
/// usage would be:
///
/// ```rust,no_run
/// # peace_item_spec_sh_cmd::ShCmd::new("sh")
/// .arg("-C")
/// .arg("/path/to/repo")
/// # ;
/// ```
///
/// To pass multiple arguments see [`args`].
///
/// [`args`]: ShCmd::args
///
/// Note that the argument is not passed through a shell, but given
/// literally to the program. This means that shell syntax like quotes,
/// escaped characters, word splitting, glob patterns, substitution, etc.
/// have no effect.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust,no_run
/// use peace_item_spec_sh_cmd::ShCmd;
///
/// let sh_cmd = ShCmd::new("ls").arg("-l").arg("-a");
/// ```
pub fn arg<S: Into<OsString>>(mut self, arg: S) -> Self {
self.args.push(arg.into());
self
}
/// Adds multiple arguments to pass to the program.
///
/// To pass a single argument see [`arg`].
///
/// [`arg`]: ShCmd::arg
///
/// Note that the arguments are not passed through a shell, but given
/// literally to the program. This means that shell syntax like quotes,
/// escaped characters, word splitting, glob patterns, substitution, etc.
/// have no effect.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust,no_run
/// use peace_item_spec_sh_cmd::ShCmd;
///
/// let sh_cmd = ShCmd::new("ls").args(["-l", "-a"]);
/// ```
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
args.into_iter().for_each(|arg| {
self.args.push(arg.into());
});
self
}
}
impl From<&ShCmd> for Command {
fn from(sh_cmd: &ShCmd) -> Command {
let mut command = Command::new(&sh_cmd.program);
command.args(&sh_cmd.args);
command
}
}
impl fmt::Display for ShCmd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.program.to_string_lossy().fmt(f)?;
self.args
.iter()
.map(|arg| arg.to_string_lossy())
.try_for_each(|arg| write!(f, " {arg}"))?;
Ok(())
}
}