command_stream/macros.rs
1//! Macros for ergonomic command execution
2//!
3//! This module provides command execution macros that offer a similar experience
4//! to JavaScript's `$` tagged template literal for shell command execution.
5//!
6//! ## Available Macros
7//!
8//! - `s!` - Short, concise macro (recommended for most use cases)
9//! - `sh!` - Shell macro (alternative short form)
10//! - `cmd!` - Command macro (explicit name)
11//! - `cs!` - Command-stream macro (another alternative)
12//!
13//! All macros are aliases and provide identical functionality.
14//!
15//! ## Usage
16//!
17//! ```rust,no_run
18//! use command_stream::s;
19//!
20//! #[tokio::main]
21//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
22//! // Simple command
23//! let result = s!("echo hello world").await?;
24//!
25//! // With interpolation (values are automatically quoted for safety)
26//! let name = "John Doe";
27//! let result = s!("echo Hello, {}", name).await?;
28//!
29//! // Multiple arguments
30//! let file = "test.txt";
31//! let dir = "/tmp";
32//! let result = s!("cp {} {}", file, dir).await?;
33//!
34//! Ok(())
35//! }
36//! ```
37
38/// Build a shell command with interpolated values safely quoted
39///
40/// This function is used internally by the `cmd!` macro to build
41/// shell commands with properly quoted interpolated values.
42pub fn build_shell_command(parts: &[&str], values: &[&str]) -> String {
43 let mut result = String::new();
44
45 for (i, part) in parts.iter().enumerate() {
46 result.push_str(part);
47 if i < values.len() {
48 result.push_str(&crate::quote::quote(values[i]));
49 }
50 }
51
52 result
53}
54
55/// Helper function to create a ProcessRunner from a command string
56pub fn create_runner(command: String) -> crate::ProcessRunner {
57 crate::ProcessRunner::new(
58 command,
59 crate::RunOptions {
60 mirror: true,
61 capture: true,
62 ..Default::default()
63 },
64 )
65}
66
67/// Helper function to create a ProcessRunner with custom options
68pub fn create_runner_with_options(
69 command: String,
70 options: crate::RunOptions,
71) -> crate::ProcessRunner {
72 crate::ProcessRunner::new(command, options)
73}
74
75/// The `cmd!` macro for ergonomic shell command execution
76///
77/// This macro provides a similar experience to JavaScript's `$` tagged template literal.
78/// Values interpolated into the command are automatically quoted for shell safety.
79///
80/// Note: Consider using the shorter `s!` or `sh!` aliases for more concise code.
81///
82/// # Examples
83///
84/// ```rust,no_run
85/// use command_stream::s;
86///
87/// # async fn example() -> Result<(), command_stream::Error> {
88/// // Simple command (returns a future that can be awaited)
89/// let result = s!("echo hello").await?;
90///
91/// // With string interpolation
92/// let name = "world";
93/// let result = s!("echo hello {}", name).await?;
94///
95/// // With multiple values
96/// let src = "source.txt";
97/// let dst = "dest.txt";
98/// let result = s!("cp {} {}", src, dst).await?;
99///
100/// // Values with special characters are automatically quoted
101/// let filename = "file with spaces.txt";
102/// let result = s!("cat {}", filename).await?; // Safely handles spaces
103/// # Ok(())
104/// # }
105/// ```
106///
107/// # Safety
108///
109/// All interpolated values are automatically quoted using shell-safe quoting,
110/// preventing command injection attacks.
111#[macro_export]
112macro_rules! cmd {
113 // No interpolation - just a plain command string
114 ($cmd:expr) => {{
115 async {
116 $crate::run($cmd).await
117 }
118 }};
119
120 // With format-style interpolation
121 ($fmt:expr, $($arg:expr),+ $(,)?) => {{
122 // Build command with quoted values
123 let mut result = String::new();
124 let values: Vec<String> = vec![$(format!("{}", $arg)),+];
125 let values_ref: Vec<&str> = values.iter().map(|s| s.as_str()).collect();
126 let fmt_parts: Vec<&str> = $fmt.split("{}").collect();
127 for (i, part) in fmt_parts.iter().enumerate() {
128 result.push_str(part);
129 if i < values_ref.len() {
130 result.push_str(&$crate::quote::quote(values_ref[i]));
131 }
132 }
133
134 async move {
135 $crate::run(result).await
136 }
137 }};
138}
139
140/// The `sh!` macro - alias for `cmd!`
141///
142/// This is an alternative name for `cmd!` that some users may find
143/// more intuitive for shell command execution.
144#[macro_export]
145macro_rules! sh {
146 ($($args:tt)*) => {
147 $crate::cmd!($($args)*)
148 };
149}
150
151/// The `s!` macro - short alias for `cmd!`
152///
153/// This is a concise alternative to `cmd!` for quick shell command execution.
154/// Recommended for use in documentation and examples.
155#[macro_export]
156macro_rules! s {
157 ($($args:tt)*) => {
158 $crate::cmd!($($args)*)
159 };
160}
161
162/// The `cs!` macro - alias for `cmd!`
163///
164/// Short for "command-stream", this provides another alternative
165/// for shell command execution.
166#[macro_export]
167macro_rules! cs {
168 ($($args:tt)*) => {
169 $crate::cmd!($($args)*)
170 };
171}