command_stream/process_argv.rs
1//! Exact-argv construction for [`ProcessRunner`] (issue #230).
2
3use std::ffi::OsString;
4
5use crate::stream_process::StreamingCommand;
6use crate::{ProcessRunner, RunOptions};
7
8impl ProcessRunner {
9 /// Create a runner that executes `program` with exactly `args`, without a
10 /// shell.
11 ///
12 /// Nothing is parsed, expanded or quoted, and built-in virtual commands
13 /// are never used, so `from_argv("echo", ["$HOME"])` prints `$HOME`. Every
14 /// other option behaves as with [`ProcessRunner::new`], including
15 /// [`StdinOption::Pipe`](crate::StdinOption::Pipe) with
16 /// [`write_stdin`](Self::write_stdin) and [`close_stdin`](Self::close_stdin).
17 /// [`command`](Self::command) reports the argv as a shell-quoted string.
18 ///
19 /// ```
20 /// use command_stream::{ProcessRunner, RunOptions, StdinOption};
21 ///
22 /// # #[tokio::main]
23 /// # async fn main() -> command_stream::Result<()> {
24 /// let mut runner = ProcessRunner::from_argv(
25 /// "cat",
26 /// Vec::<String>::new(),
27 /// RunOptions { stdin: StdinOption::Pipe, mirror: false, ..Default::default() },
28 /// );
29 /// runner.start().await?;
30 /// runner.write_stdin(b"{\"type\":\"user\"}\n").await?;
31 /// runner.close_stdin().await?;
32 /// let result = runner.run().await?;
33 /// assert_eq!(result.stdout, "{\"type\":\"user\"}\n");
34 /// # Ok(())
35 /// # }
36 /// ```
37 pub fn from_argv<P, I, S>(program: P, args: I, options: RunOptions) -> Self
38 where
39 P: Into<OsString>,
40 I: IntoIterator<Item = S>,
41 S: Into<OsString>,
42 {
43 let program = program.into();
44 let args: Vec<OsString> = args.into_iter().map(Into::into).collect();
45 let display = std::iter::once(&program)
46 .chain(&args)
47 .map(|part| crate::quote::quote(&part.to_string_lossy()))
48 .collect::<Vec<_>>()
49 .join(" ");
50 let mut runner = Self::new(display, options);
51 runner.argv = Some(StreamingCommand::Argv { program, args });
52 runner
53 }
54}