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}