1use std::ffi::OsStr;
22use std::io;
23use std::ops::Deref;
24use std::path::Path;
25use std::process::{self, Child, ExitStatus, Output, Stdio};
26
27mod constants;
28#[cfg(not(unix))]
29mod other;
30#[cfg(unix)]
31mod unix;
32
33#[cfg(not(unix))]
34use other as platform;
35#[cfg(unix)]
36use unix as platform;
37
38#[cfg(test)]
39mod experimental_limit;
40
41#[derive(Debug)]
42pub struct Command {
43 inner: process::Command,
44 remaining_argument_length: i64,
45}
46
47impl Command {
48 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
50 Command {
51 inner: process::Command::new(&program),
52 remaining_argument_length: platform::available_argument_length([program].iter())
53 .unwrap_or(constants::REASONABLE_DEFAULT_LENGTH),
54 }
55 }
56
57 fn e2big() -> io::Error {
59 #[cfg(unix)]
61 return io::Error::from_raw_os_error(libc::E2BIG);
62 #[cfg(not(unix))]
63 return io::ErrorKind::Other.into();
64 }
65
66 fn check_size<I, S>(&self, args: I) -> io::Result<i64>
68 where
69 I: IntoIterator<Item = S>,
70 S: AsRef<OsStr>,
71 {
72 let mut size = 0;
73 for arg in args {
74 let arg = arg.as_ref();
75 if arg.len() as i64 > platform::max_single_argument_length() {
76 return Err(Self::e2big());
77 }
78 size += platform::arg_size(arg);
79 }
80
81 if size > self.remaining_argument_length {
82 return Err(Self::e2big());
83 }
84
85 Ok(size)
86 }
87
88 pub fn arg_would_fit<S: AsRef<OsStr>>(&self, arg: S) -> bool {
90 self.args_would_fit(&[arg])
91 }
92
93 pub fn args_would_fit<I, S>(&self, args: I) -> bool
95 where
96 I: IntoIterator<Item = S>,
97 S: AsRef<OsStr>,
98 {
99 self.check_size(args).is_ok()
100 }
101
102 pub fn try_arg<S: AsRef<OsStr>>(&mut self, arg: S) -> io::Result<&mut Self> {
105 self.try_args(&[arg])
106 }
107
108 pub fn try_args<I, S>(&mut self, args: I) -> io::Result<&mut Self>
111 where
112 I: IntoIterator<Item = S> + Copy,
113 S: AsRef<OsStr>,
114 {
115 let size = self.check_size(args)?;
116 self.inner.args(args);
117 self.remaining_argument_length -= size;
118 Ok(self)
119 }
120
121 pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
123 self.inner.current_dir(dir);
124 self
125 }
126
127 pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
129 self.inner.stdin(cfg);
130 self
131 }
132
133 pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
135 self.inner.stdout(cfg);
136 self
137 }
138
139 pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
141 self.inner.stderr(cfg);
142 self
143 }
144
145 pub fn spawn(&mut self) -> io::Result<Child> {
147 self.inner.spawn()
148 }
149
150 pub fn output(&mut self) -> io::Result<Output> {
152 self.inner.output()
153 }
154
155 pub fn status(&mut self) -> io::Result<ExitStatus> {
157 self.inner.status()
158 }
159}
160
161impl Deref for Command {
162 type Target = process::Command;
163
164 fn deref(&self) -> &Self::Target {
165 &self.inner
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_api() {
175 let mut cmd = Command::new("echo");
176 cmd.try_arg("Hello").expect("try_arg() to succeed");
177 cmd.try_args(&["world", "!"])
178 .expect("try_args() to succeed");
179
180 cmd.current_dir(".");
181
182 cmd.stdin(Stdio::inherit());
183 cmd.stdout(Stdio::piped());
184 cmd.stderr(Stdio::null());
185
186 let mut output = cmd
187 .spawn()
188 .expect("spawn() to succeed")
189 .wait_with_output()
190 .expect("wait_with_output() to succeed");
191 assert!(output.stdout.len() > 13);
192 assert!(output.status.success());
193
194 output = cmd.output().expect("output() to succeed");
195 assert!(output.stdout.len() > 13);
196 assert!(output.status.success());
197
198 let status = cmd.status().expect("status() to succeed");
199 assert!(status.success());
200 }
201}