cradle/
macros.rs

1/// Executes a child process without capturing any output.
2///
3/// ```
4/// # let temp_dir = tempfile::TempDir::new().unwrap();
5/// # std::env::set_current_dir(&temp_dir).unwrap();
6/// use cradle::prelude::*;
7///
8/// run!(%"touch ./foo");
9/// ```
10///
11/// If an error occurs, `run!` will panic.
12/// See [`crate::error::Error`] for possible errors.
13///
14/// For capturing output from child processes, see [`crate::run_output!`].
15#[macro_export]
16macro_rules! run {
17    ($($args:tt)*) => {{
18        $crate::input::Input::run($crate::tuple_up!($($args)*))
19    }}
20}
21
22/// Execute child processes, and capture some output.
23/// For example you can capture what the child process writes to stdout:
24///
25/// ```
26/// use cradle::prelude::*;
27///
28/// let StdoutUntrimmed(output) = run_output!(%"echo foo");
29/// assert_eq!(output, "foo\n");
30/// ```
31///
32/// [`run_output!`] uses return-type polymorphism.
33/// So by using a different return type,
34/// you can control what outputs of child processes you want to capture.
35/// Here's an example to capture an exit code:
36///
37/// ```
38/// use cradle::prelude::*;
39///
40/// let Status(status) = run_output!("false");
41/// assert_eq!(status.code(), Some(1));
42/// ```
43///
44/// You can use any type that implements [`crate::output::Output`] as the return type.
45/// See the module documentation for more comprehensive documentation.
46#[macro_export]
47macro_rules! run_output {
48    ($($args:tt)*) => {{
49        $crate::input::Input::run_output($crate::tuple_up!($($args)*))
50    }}
51}
52
53/// Like [`run_output!`], but fixes the return type to [`Result<T, Error>`],
54/// where `T` is any type that implements [`Output`](crate::Output).
55#[macro_export]
56macro_rules! run_result {
57    ($($args:tt)*) => {{
58        $crate::input::Input::run_result($crate::tuple_up!($($args)*))
59    }}
60}
61
62#[doc(hidden)]
63#[macro_export]
64macro_rules! tuple_up {
65    (% $last:expr $(,)?) => {
66        $crate::input::Split($last)
67    };
68    ($last:expr $(,)?) => {
69        $last
70    };
71    (% $head:expr, $($tail:tt)*) => {
72        ($crate::input::Split($head), $crate::tuple_up!($($tail)*))
73    };
74    ($head:expr, $($tail:tt)*) => {
75        ($head, $crate::tuple_up!($($tail)*))
76    };
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::prelude::*;
82
83    mod tuple_up {
84        use super::*;
85
86        #[test]
87        #[allow(clippy::eq_op)]
88        fn one_value() {
89            assert_eq!(tuple_up!(1), 1);
90        }
91
92        #[test]
93        fn two_values() {
94            assert_eq!(tuple_up!(1, 2), (1, 2));
95        }
96
97        #[test]
98        fn three_values() {
99            assert_eq!(tuple_up!(1, 2, 3), (1, (2, 3)));
100        }
101
102        #[test]
103        fn nested_tuples() {
104            assert_eq!(tuple_up!(1, (2, 3), 4), (1, ((2, 3), 4)));
105        }
106
107        #[test]
108        fn percent_shortcut() {
109            assert_eq!(tuple_up!(%"foo"), Split("foo"));
110        }
111
112        #[test]
113        fn percent_shortcut_with_subsequent_values() {
114            assert_eq!(tuple_up!(%"foo", "bar"), (Split("foo"), "bar"));
115        }
116
117        #[test]
118        fn percent_shortcut_with_preceeding_values() {
119            assert_eq!(tuple_up!("foo", %"bar"), ("foo", Split("bar")));
120        }
121
122        #[test]
123        fn percent_shortcut_with_multiple_values() {
124            assert_eq!(
125                tuple_up!(%"foo", "bar", %"baz"),
126                (Split("foo"), ("bar", Split("baz")))
127            );
128        }
129    }
130}