1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
//! A helper library for building commands. //! //! The [`cmd!`] macro can be used to generate [`std::process::Command`] (or [`PipeCommand`]). Refer //! to its documentation for more information. //! //! # Examples //! //! ```rust //! use procmd::cmd; //! use std::path::Path; //! //! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! let file_path = Path::new("/path/to/file"); //! cmd!("cat", "-n", file_path).spawn()?; //! # Ok(()) //! # } //! ``` #![feature(min_const_generics)] #![forbid(unsafe_code)] #![warn(rust_2018_idioms, missing_docs, missing_debug_implementations)] use std::io; use std::process::{Child, Command, ExitStatus, Output, Stdio}; /// A macro for building commands. /// /// # Generating a simple command /// /// To generate a [`std::process::Command`], the program and additional arguments can be passed to /// the macro. /// /// ## Example /// /// The invocation: /// /// ```rust /// # use procmd::cmd; /// let cmd = cmd!("ls", "-a", "/"); /// ``` /// /// expands to: /// /// ```rust /// let cmd = { /// let mut cmd = ::std::process::Command::new("ls"); /// cmd.arg("-a"); /// cmd.arg("/"); /// cmd /// }; /// ``` /// /// # Generating a piped command /// /// To generate a [`PipeCommand`], multiple programs and arguments seperated by `=>` can be passed /// to the macro. /// /// [`PipeCommand`]: crate::PipeCommand /// /// ## Example /// /// The invocation: /// /// ```rust /// # use procmd::cmd; /// let pipe_cmd = cmd!("ls" => "grep", "test" => "wc", "-l"); /// ``` /// /// expands to: /// /// ```rust /// # use procmd::cmd; /// let pipe_cmd = ::procmd::PipeCommand::new([ /// { /// let mut cmd = ::std::process::Command::new("ls"); /// cmd /// }, /// { /// let mut cmd = ::std::process::Command::new("grep"); /// cmd.arg("test"); /// cmd /// }, /// { /// let mut cmd = ::std::process::Command::new("wc"); /// cmd.arg("-l"); /// cmd /// }, /// ]); /// ``` pub use procmd_macro::cmd; /// Multiple commands that will be piped. /// /// A [`PipeCommand`] can be created by either using the [`new`] method or by using the [`cmd!`] /// macro. /// /// # Examples /// /// Using the [`new`] method combined with [`Command::new`] and a simple use of the [`cmd!`] macro: /// /// ```rust /// use procmd::{cmd, PipeCommand}; /// use std::process::Command; /// /// # fn main() -> Result<(), std::io::Error> { /// let mut pipe_cmd = PipeCommand::new([Command::new("ls"), cmd!("grep", "example")]); /// let child = pipe_cmd.spawn()?; /// # Ok(()) /// # } /// ``` /// /// Using the [`cmd!`] macro with the `=>` token to generate a [`PipeCommand`] and calling the /// [`status`] method to get the exit status: /// /// ```rust /// use procmd::cmd; /// /// # fn main() -> Result<(), std::io::Error> { /// let mut pipe_cmd = cmd!("ls" => "grep", "example"); /// let exit_status = pipe_cmd.status()?; /// # Ok(()) /// # } /// ``` /// /// [`new`]: Self::new /// [`status`]: Self::status /// [`cmd!`]: crate::cmd #[derive(Debug)] pub struct PipeCommand<const N: usize> { /// The commands. pub commands: [Command; N], } impl<const N: usize> PipeCommand<N> { /// Creates a new [`PipeCommand`]. pub fn new(commands: [Command; N]) -> Self { Self { commands } } /// Spawns all commands except the last one and calls `f` on the last command. /// /// # Panics /// /// This method panics if [`commands`] is empty. /// /// [`commands`]: Self::commands fn run<F, U>(&mut self, f: F) -> io::Result<U> where F: Fn(&mut Command) -> io::Result<U>, { let mut child: Option<Child> = None; let commands_len = self.commands.len(); for (i, command) in self.commands[..commands_len - 1].iter_mut().enumerate() { if let Some(child) = child { command.stdin(child.stdout.unwrap()); } if i == commands_len - 1 { break; } else { command.stdout(Stdio::piped()); child = Some(command.spawn()?); } } f(&mut self.commands[commands_len - 1]) } /// Spawns all commands and returns the [`Child`] of the last command. /// /// # Panics /// /// This method panics if [`commands`] is empty. /// /// [`commands`]: Self::commands pub fn spawn(&mut self) -> io::Result<Child> { self.run(|command| command.spawn()) } /// Returns the [`Output`] of the last command. /// /// Note that this method still calls [`Command::spawn`] on all commands except the last one. /// /// # Panics /// /// This method panics if [`commands`] is empty. /// /// [`commands`]: Self::commands pub fn output(&mut self) -> io::Result<Output> { self.run(|command| command.output()) } /// Returns the [`ExitStatus`] of the last command. /// /// Note that this method still calls [`Command::spawn`] on all commands except the last one. /// /// # Panics /// /// This method panics if [`commands`] is empty. /// /// [`commands`]: Self::commands pub fn status(&mut self) -> io::Result<ExitStatus> { self.run(|command| command.status()) } }