cradle/input.rs
1//! The [`Input`] trait that defines all possible inputs to a child process.
2
3use crate::{
4 child_output::ChildOutput,
5 config::Config,
6 context::Context,
7 error::{panic_on_error, Error},
8 output::Output,
9};
10use std::{
11 ffi::{OsStr, OsString},
12 io::Write,
13 path::{Path, PathBuf},
14 sync::Arc,
15};
16
17/// All types that are possible arguments to [`run!`], [`run_output!`] or
18/// [`run_result!`] must implement this trait.
19/// This makes `cradle` very flexible.
20/// For example you can pass in an executable as a String,
21/// and a variable number of arguments as a [`Vec`]:
22///
23/// ```
24/// use cradle::prelude::*;
25///
26/// let executable = "echo";
27/// let arguments = vec!["foo", "bar"];
28/// let StdoutUntrimmed(output) = run_output!(executable, arguments);
29/// assert_eq!(output, "foo bar\n");
30/// ```
31///
32/// For more documentation on all possible input types,
33/// see the documentation for the individual impls of [`Input`].
34/// Here's a non-exhaustive list of the most commonly used types to get you started:
35///
36/// - [`String`] and [`&str`],
37/// - [`Split`] (and its shortcut `%`) to split commands by whitespace,
38/// - [`PathBuf`] and [`&Path`],
39/// - multiple sequence types, like [`vectors`], [`slices`] and (since version 1.51) [`arrays`],
40/// - [`CurrentDir`],
41/// - [`Env`] for setting environment variables,
42/// - [`Stdin`], and
43/// - [`LogCommand`].
44///
45/// [`String`]: trait.Input.html#impl-Input-for-String
46/// [`&str`]: trait.Input.html#impl-Input-for-%26str
47/// [`PathBuf`]: trait.Input.html#impl-Input-for-PathBuf
48/// [`&Path`]: trait.Input.html#impl-Input-for-%26Path
49/// [`vectors`]: trait.Input.html#impl-Input-for-Vec<T>
50/// [`slices`]: trait.Input.html#impl-Input-for-%26[T]
51/// [`arrays`]: trait.Input.html#impl-Input-for-[T%3B%20N]
52///
53/// ## Tuples
54///
55/// `cradle` also implements [`Input`] for tuples of types that themselves implement [`Input`].
56/// Instead of passing multiple arguments to [`run!`], they can be passed in a single tuple:
57///
58/// ```
59/// use cradle::prelude::*;
60///
61/// let args = ("echo", "foo");
62/// let StdoutTrimmed(output) = run_output!(args);
63/// assert_eq!(output, "foo");
64/// ```
65///
66/// This can be used to group arguments:
67///
68/// ```
69/// use cradle::prelude::*;
70///
71/// let to_hex_command = ("xxd", "-ps", "-u", LogCommand);
72/// let StdoutTrimmed(output) = run_output!(to_hex_command, Stdin(&[14, 15, 16]));
73/// assert_eq!(output, "0E0F10");
74/// ```
75///
76/// Also, tuples make it possible to write wrappers around [`run!`] without requiring the use of macros:
77///
78/// ```
79/// use cradle::prelude::*;
80///
81/// fn to_hex<I: Input>(input: I) -> String {
82/// let StdoutTrimmed(hex) = run_output!(%"xxd -ps -u", input);
83/// hex
84/// }
85///
86/// // It works for slices:
87/// let hex = to_hex(Stdin(&[14, 15, 16]));
88/// assert_eq!(hex, "0E0F10");
89///
90/// // Vectors:
91/// let hex = to_hex(Stdin(vec![14, 15, 16]));
92/// assert_eq!(hex, "0E0F10");
93///
94/// // And multiple arguments using tuples:
95/// let hex = to_hex((Stdin(&[14, 15, 16]), Stdin(&[17, 18, 19])));
96/// assert_eq!(hex, "0E0F10111213");
97/// ```
98///
99/// ## Custom [`Input`] impls
100///
101/// The provided `Input` implementations should be sufficient for most use cases,
102/// but custom `Input` implementations can be written to extend `cradle`.
103///
104/// Here's an example of an `Environment` type, that wraps
105/// [`BTreeMap`](std::collections::BTreeMap) and adds all contained
106/// key-value pairs to the environment of the child process.
107///
108/// ```
109/// use cradle::prelude::*;
110/// use cradle::config::Config;
111/// use std::collections::BTreeMap;
112///
113/// struct Environment(BTreeMap<String, String>);
114///
115/// impl Environment {
116/// fn new() -> Self {
117/// Environment(BTreeMap::new())
118/// }
119///
120/// fn add(mut self, key: &str, value: &str) -> Self {
121/// self.0.insert(key.to_owned(), value.to_owned());
122/// self
123/// }
124/// }
125///
126/// impl Input for Environment {
127/// fn configure(self, config: &mut Config) {
128/// for (key, value) in self.0.into_iter() {
129/// Env(key, value).configure(config)
130/// }
131/// }
132/// }
133///
134/// let env_vars = Environment::new()
135/// .add("FOO", "foo")
136/// .add("BAR", "bar");
137///
138/// let StdoutUntrimmed(output) = run_output!("env", env_vars);
139/// assert!(output.contains("FOO=foo\n"));
140/// assert!(output.contains("BAR=bar\n"));
141/// ```
142///
143/// It is not recommended to override [`run`](Input::run),
144/// [`run_output`](Input::run_output) or [`run_result`](Input::run_result).
145///
146/// Also note that all fields of the type [`Config`] are private.
147/// That means that when you're writing your own [`Input`] impls,
148/// you _have_ to implement the [`Input::configure`] method
149/// of your type in terms of the [`Input::configure`] methods
150/// of the various [`Input`] types that `cradle` provides --
151/// as demonstrated in the code snippet above.
152/// [`Config`]'s fields are private to allow to add new features to `cradle`
153/// without introducing breaking API changes.
154pub trait Input: Sized {
155 /// Configures the given [`Config`](crate::config::Config) for the [`Input`] `self`.
156 /// Usually you won't have to write your own custom impls for [`Input`],
157 /// nor call this function yourself.
158 /// So you can safely ignore this method.
159 ///
160 /// See also [Custom `Input` impls](#custom-input-impls).
161 fn configure(self, config: &mut Config);
162
163 /// `input.run()` runs `input` as a child process.
164 /// It's equivalent to `run!(input)`.
165 ///
166 /// ```
167 /// # let temp_dir = tempfile::TempDir::new().unwrap();
168 /// # std::env::set_current_dir(&temp_dir).unwrap();
169 /// use cradle::prelude::*;
170 ///
171 /// ("touch", "foo").run();
172 /// ```
173 #[rustversion::attr(since(1.46), track_caller)]
174 fn run(self) {
175 self.run_output()
176 }
177
178 /// `input.run()` runs `input` as a child process.
179 /// It's equivalent to `run_output!(input)`.
180 ///
181 /// ```
182 /// use cradle::prelude::*;
183 ///
184 /// let StdoutTrimmed(output) = ("echo", "foo").run_output();
185 /// assert_eq!(output, "foo");
186 /// ```
187 #[rustversion::attr(since(1.46), track_caller)]
188 fn run_output<O>(self) -> O
189 where
190 O: Output,
191 {
192 panic_on_error(self.run_result())
193 }
194
195 /// `input.run_result()` runs `input` as a child process.
196 /// It's equivalent to `run_result!(input)`.
197 ///
198 /// ```
199 /// use cradle::prelude::*;
200 ///
201 /// # fn build() -> Result<(), Error> {
202 /// // make sure build tools are installed
203 /// run_result!(%"which make")?;
204 /// run_result!(%"which gcc")?;
205 /// run_result!(%"which ld")?;
206 /// run_result!(%"make build")?;
207 /// # Ok(())
208 /// # }
209 /// ```
210 fn run_result<O>(self) -> Result<O, crate::error::Error>
211 where
212 O: Output,
213 {
214 let context = Context::production();
215 run_result_with_context(context, self)
216 }
217}
218
219pub(crate) fn run_result_with_context<Stdout, Stderr, I, O>(
220 context: Context<Stdout, Stderr>,
221 input: I,
222) -> Result<O, Error>
223where
224 Stdout: Write + Clone + Send + 'static,
225 Stderr: Write + Clone + Send + 'static,
226 I: Input,
227 O: Output,
228{
229 let mut config = Config::default();
230 input.configure(&mut config);
231 ChildOutput::run_child_process_output(context, config)
232}
233
234#[cfg(test)]
235pub(crate) fn run_result_with_context_unit<Stdout, Stderr, I>(
236 context: Context<Stdout, Stderr>,
237 input: I,
238) -> Result<(), Error>
239where
240 Stdout: Write + Clone + Send + 'static,
241 Stderr: Write + Clone + Send + 'static,
242 I: Input,
243{
244 run_result_with_context(context, input)
245}
246
247/// Blanket implementation for `&_`.
248impl<T> Input for &T
249where
250 T: Input + Clone,
251{
252 #[doc(hidden)]
253 fn configure(self, config: &mut Config) {
254 self.clone().configure(config);
255 }
256}
257
258/// Arguments of type [`OsString`] are passed to the child process
259/// as arguments.
260///
261/// ```
262/// use cradle::prelude::*;
263///
264/// run!("ls", std::env::var_os("HOME").unwrap());
265/// ```
266impl Input for OsString {
267 #[doc(hidden)]
268 fn configure(self, config: &mut Config) {
269 config.arguments.push(self);
270 }
271}
272
273/// Arguments of type [`&OsStr`] are passed to the child process
274/// as arguments.
275///
276/// ```
277/// use cradle::prelude::*;
278///
279/// run!("echo", std::env::current_dir().unwrap().file_name().unwrap());
280/// ```
281///
282/// [`&OsStr`]: std::ffi::OsStr
283impl Input for &OsStr {
284 #[doc(hidden)]
285 fn configure(self, config: &mut Config) {
286 self.to_os_string().configure(config);
287 }
288}
289
290/// Arguments of type [`&str`] are passed to the child process as arguments.
291/// This is especially useful because it allows you to use string literals:
292///
293/// ```
294/// use cradle::prelude::*;
295///
296/// let StdoutTrimmed(output) = run_output!("echo", "foo");
297/// assert_eq!(output, "foo");
298/// ```
299impl Input for &str {
300 #[doc(hidden)]
301 fn configure(self, config: &mut Config) {
302 OsStr::new(self).configure(config);
303 }
304}
305
306/// Arguments of type [`String`] are passed to the child process
307/// as arguments. Executables can also be passed as [`String`]s:
308///
309/// ```
310/// use cradle::prelude::*;
311///
312/// let executable: String = "echo".to_string();
313/// let argument: String = "foo".to_string();
314/// let StdoutTrimmed(output) = run_output!(executable, argument);
315/// assert_eq!(output, "foo");
316/// ```
317impl Input for String {
318 #[doc(hidden)]
319 fn configure(self, config: &mut Config) {
320 OsString::from(self).configure(config);
321 }
322}
323
324/// Splits the contained string by whitespace (using [`split_whitespace`])
325/// and uses the resulting words as separate arguments.
326///
327/// ```
328/// use cradle::prelude::*;
329///
330/// let StdoutTrimmed(output) = run_output!(Split("echo foo"));
331/// assert_eq!(output, "foo");
332/// ```
333///
334/// Since this is such a common case, `cradle` also provides a syntactic shortcut
335/// for [`Split`], the `%` symbol:
336///
337/// ```
338/// use cradle::prelude::*;
339///
340/// let StdoutTrimmed(output) = run_output!(%"echo foo");
341/// assert_eq!(output, "foo");
342/// ```
343///
344/// [`split_whitespace`]: str::split_whitespace
345#[derive(Debug, PartialEq, Clone)]
346pub struct Split(pub &'static str);
347
348impl Input for crate::input::Split {
349 #[doc(hidden)]
350 fn configure(self, config: &mut Config) {
351 for argument in self.0.split_whitespace() {
352 argument.configure(config);
353 }
354 }
355}
356
357/// Allows to use [`split`] to split your argument into words:
358///
359/// ```
360/// use cradle::prelude::*;
361///
362/// let StdoutTrimmed(output) = run_output!("echo foo".split(' '));
363/// assert_eq!(output, "foo");
364/// ```
365///
366/// Arguments to [`split`] must be of type [`char`].
367///
368/// [`split`]: str::split
369impl Input for std::str::Split<'static, char> {
370 #[doc(hidden)]
371 fn configure(self, config: &mut Config) {
372 for word in self {
373 word.configure(config);
374 }
375 }
376}
377
378/// Allows to use [`split_whitespace`] to split your argument into words:
379///
380/// ```
381/// use cradle::prelude::*;
382///
383/// let StdoutTrimmed(output) = run_output!("echo foo".split_whitespace());
384/// assert_eq!(output, "foo");
385/// ```
386///
387/// [`split_whitespace`]: str::split_whitespace
388impl Input for std::str::SplitWhitespace<'static> {
389 #[doc(hidden)]
390 fn configure(self, config: &mut Config) {
391 for word in self {
392 word.configure(config);
393 }
394 }
395}
396
397/// Allows to use [`split_ascii_whitespace`] to split your argument into words:
398///
399/// ```
400/// use cradle::prelude::*;
401///
402/// let StdoutTrimmed(output) = run_output!("echo foo".split_ascii_whitespace());
403/// assert_eq!(output, "foo");
404/// ```
405///
406/// [`split_ascii_whitespace`]: str::split_ascii_whitespace
407impl Input for std::str::SplitAsciiWhitespace<'static> {
408 #[doc(hidden)]
409 fn configure(self, config: &mut Config) {
410 for word in self {
411 word.configure(config);
412 }
413 }
414}
415
416impl Input for () {
417 #[doc(hidden)]
418 fn configure(self, _: &mut Config) {}
419}
420
421macro_rules! tuple_impl {
422 ($($index:tt, $generics:ident,)+) => {
423 impl<$($generics),+> Input for ($($generics,)+)
424 where
425 $($generics: Input,)+
426 {
427 #[doc(hidden)]
428 fn configure(self, config: &mut Config) {
429 $(<$generics as Input>::configure(self.$index, config);)+
430 }
431 }
432 };
433}
434
435tuple_impl!(0, A,);
436tuple_impl!(0, A, 1, B,);
437tuple_impl!(0, A, 1, B, 2, C,);
438tuple_impl!(0, A, 1, B, 2, C, 3, D,);
439tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E,);
440tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F,);
441tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G,);
442tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H,);
443tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I,);
444tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J,);
445tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K,);
446tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K, 11, L,);
447tuple_impl!(0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K, 11, L, 12, M,);
448tuple_impl!(
449 0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K, 11, L, 12, M, 13, N,
450);
451tuple_impl!(
452 0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K, 11, L, 12, M, 13, N, 14, O,
453);
454tuple_impl!(
455 0, A, 1, B, 2, C, 3, D, 4, E, 5, F, 6, G, 7, H, 8, I, 9, J, 10, K, 11, L, 12, M, 13, N, 14, O,
456 15, P,
457);
458
459/// All elements of the given [`Vec`] are used as arguments to the child process.
460/// Same as passing in the elements separately.
461///
462/// ```
463/// use cradle::prelude::*;
464///
465/// let StdoutTrimmed(output) = run_output!(vec!["echo", "foo"]);
466/// assert_eq!(output, "foo");
467/// ```
468impl<T> Input for Vec<T>
469where
470 T: Input,
471{
472 #[doc(hidden)]
473 fn configure(self, config: &mut Config) {
474 for t in self.into_iter() {
475 t.configure(config);
476 }
477 }
478}
479
480/// Similar to the implementation for [`Vec<T>`].
481/// All elements of the array will be used as arguments.
482///
483/// ```
484/// use cradle::prelude::*;
485///
486/// let StdoutTrimmed(output) = run_output!(["echo", "foo"]);
487/// assert_eq!(output, "foo");
488/// ```
489///
490/// Only works on rust version `1.51` and up.
491#[rustversion::since(1.51)]
492impl<T, const N: usize> Input for [T; N]
493where
494 T: Input,
495{
496 #[doc(hidden)]
497 fn configure(self, config: &mut Config) {
498 #[rustversion::before(1.59)]
499 fn array_to_iter<T, const N: usize>(array: [T; N]) -> impl Iterator<Item = T> {
500 std::array::IntoIter::new(array)
501 }
502 #[rustversion::since(1.59)]
503 fn array_to_iter<T, const N: usize>(array: [T; N]) -> impl Iterator<Item = T> {
504 IntoIterator::into_iter(array)
505 }
506
507 for t in array_to_iter(self) {
508 t.configure(config);
509 }
510 }
511}
512
513/// Similar to the implementation for [`Vec<T>`].
514/// All elements of the slice will be used as arguments.
515impl<T> Input for &[T]
516where
517 T: Input + Clone,
518{
519 #[doc(hidden)]
520 fn configure(self, config: &mut Config) {
521 self.to_vec().configure(config);
522 }
523}
524
525/// Passing in [`LogCommand`] as an argument to `cradle` will cause it
526/// to log the commands (including all arguments) to `stderr`.
527/// (This is similar `bash`'s `-x` option.)
528///
529/// ```
530/// use cradle::prelude::*;
531///
532/// run!(LogCommand, %"echo foo");
533/// // writes '+ echo foo' to stderr
534/// ```
535#[derive(Debug, Clone, Copy)]
536pub struct LogCommand;
537
538impl Input for LogCommand {
539 #[doc(hidden)]
540 fn configure(self, config: &mut Config) {
541 config.log_command = true;
542 }
543}
544
545/// By default child processes inherit the current directory from their
546/// parent. You can override this with [`CurrentDir`]:
547///
548/// ```
549/// use cradle::prelude::*;
550///
551/// # #[cfg(target_os = "linux")]
552/// # {
553/// let StdoutTrimmed(output) = run_output!("pwd", CurrentDir("/tmp"));
554/// assert_eq!(output, "/tmp");
555/// # }
556/// ```
557///
558/// Paths that are relative to the parent's current directory are allowed.
559#[derive(Debug, Clone)]
560pub struct CurrentDir<T: AsRef<Path>>(pub T);
561
562impl<T> Input for CurrentDir<T>
563where
564 T: AsRef<Path>,
565{
566 #[doc(hidden)]
567 fn configure(self, config: &mut Config) {
568 config.working_directory = Some(self.0.as_ref().to_owned());
569 }
570}
571
572/// Arguments of type [`PathBuf`] are passed to the child process
573/// as arguments.
574///
575/// ```
576/// use cradle::prelude::*;
577/// use std::path::PathBuf;
578///
579/// let current_dir: PathBuf = std::env::current_dir().unwrap();
580/// run!("ls", current_dir);
581/// ```
582impl Input for PathBuf {
583 #[doc(hidden)]
584 fn configure(self, config: &mut Config) {
585 self.into_os_string().configure(config);
586 }
587}
588
589/// Arguments of type [`&Path`] are passed to the child process
590/// as arguments.
591///
592/// ```
593/// # let temp_dir = tempfile::TempDir::new().unwrap();
594/// # std::env::set_current_dir(&temp_dir).unwrap();
595/// use cradle::prelude::*;
596/// use std::path::Path;
597///
598/// let file: &Path = Path::new("./foo");
599/// run!("touch", file);
600/// ```
601///
602/// [`&Path`]: std::path::Path
603impl Input for &Path {
604 #[doc(hidden)]
605 fn configure(self, config: &mut Config) {
606 self.as_os_str().to_os_string().configure(config);
607 }
608}
609
610/// Writes the given byte slice to the child's standard input.
611///
612/// ```
613/// use cradle::prelude::*;
614///
615/// # #[cfg(target_os = "linux")]
616/// # {
617/// let StdoutUntrimmed(output) = run_output!("sort", Stdin("foo\nbar\n"));
618/// assert_eq!(output, "bar\nfoo\n");
619/// # }
620/// ```
621///
622/// If `Stdin` is used multiple times, all given bytes slices will be written
623/// to the child's standard input in order.
624#[derive(Debug, Clone)]
625pub struct Stdin<T: AsRef<[u8]>>(pub T);
626
627impl<T> Input for Stdin<T>
628where
629 T: AsRef<[u8]>,
630{
631 #[doc(hidden)]
632 fn configure(self, config: &mut Config) {
633 match config.stdin.as_mut() {
634 Some(arc) => {
635 Arc::make_mut(arc).extend_from_slice(self.0.as_ref());
636 }
637 None => {
638 config.stdin = Some(Arc::new(self.0.as_ref().to_vec()));
639 }
640 }
641 }
642}
643
644/// Adds an environment variable to the environment of the child process.
645///
646/// ```
647/// use cradle::prelude::*;
648///
649/// let StdoutUntrimmed(output) = run_output!("env", Env("FOO", "bar"));
650/// assert!(output.contains("FOO=bar\n"));
651/// ```
652///
653/// Child processes inherit the environment of the parent process.
654/// [`Env`] only adds environment variables to that inherited environment.
655/// If the environment variable is also set in the parent process,
656/// it is overwritten by [`Env`].
657#[derive(Debug, Clone)]
658pub struct Env<Key, Value>(pub Key, pub Value)
659where
660 Key: AsRef<OsStr>,
661 Value: AsRef<OsStr>;
662
663impl<Key, Value> Input for Env<Key, Value>
664where
665 Key: AsRef<OsStr>,
666 Value: AsRef<OsStr>,
667{
668 #[doc(hidden)]
669 fn configure(self, config: &mut Config) {
670 let Self(key, value) = self;
671 config
672 .added_environment_variables
673 .push((key.as_ref().to_os_string(), value.as_ref().to_os_string()));
674 }
675}