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}