rune_modules/
process.rs

1//! The native `process` module for the [Rune Language].
2//!
3//! [Rune Language]: https://rune-rs.github.io
4//!
5//! ## Usage
6//!
7//! Add the following to your `Cargo.toml`:
8//!
9//! ```toml
10//! rune-modules = { version = "0.14.1", features = ["process"] }
11//! ```
12//!
13//! Install it into your context:
14//!
15//! ```rust
16//! let mut context = rune::Context::with_default_modules()?;
17//! context.install(rune_modules::process::module(true)?)?;
18//! # Ok::<_, rune::support::Error>(())
19//! ```
20//!
21//! Use it in Rune:
22//!
23//! ```rust,ignore
24//! use process::Command;
25//!
26//! fn main() {
27//!     let command = Command::new("ls");
28//!     command.run().await;
29//! }
30//! ```
31
32// Documentation copied from the Tokio project under the MIT license.
33// See: https://github.com/tokio-rs/tokio/blob/master/LICENSE
34
35use rune::alloc::clone::TryClone;
36use rune::alloc::fmt::TryWrite;
37use rune::alloc::Vec;
38use rune::runtime::{Bytes, Formatter, Mut, Value, VmResult};
39use rune::{vm_try, vm_write, Any, ContextError, Module};
40
41use std::io;
42use tokio::process;
43
44/// A module for working with processes.
45///
46/// This allows spawning child processes, capturing their output, and creating
47/// pipelines.
48///
49/// # Tokio
50///
51/// This function is implemented using [Tokio], and requires the Tokio runtime
52/// to be in scope.
53///
54/// [Tokio]: https://tokio.rs
55#[rune::module(::process)]
56pub fn module(_stdio: bool) -> Result<Module, ContextError> {
57    let mut module = Module::from_meta(self::module_meta)?;
58
59    module.ty::<Command>()?;
60    module.function_meta(Command::new__meta)?;
61    module.function_meta(Command::arg__meta)?;
62    module.function_meta(Command::args__meta)?;
63    module.function_meta(Command::debug_fmt__meta)?;
64    #[cfg(unix)]
65    module.function_meta(Command::arg0__meta)?;
66    module.function_meta(Command::stdin__meta)?;
67    module.function_meta(Command::stdout__meta)?;
68    module.function_meta(Command::stderr__meta)?;
69    module.function_meta(Command::kill_on_drop__meta)?;
70    module.function_meta(Command::spawn__meta)?;
71
72    module.ty::<Child>()?;
73    module.function_meta(Child::debug_fmt__meta)?;
74    module.function_meta(Child::stdin__meta)?;
75    module.function_meta(Child::stdout__meta)?;
76    module.function_meta(Child::stderr__meta)?;
77    module.function_meta(Child::id__meta)?;
78    module.function_meta(Child::start_kill__meta)?;
79    module.function_meta(Child::kill__meta)?;
80    module.function_meta(Child::wait__meta)?;
81    module.function_meta(Child::wait_with_output__meta)?;
82
83    module.ty::<ExitStatus>()?;
84    module.function_meta(ExitStatus::code__meta)?;
85    module.function_meta(ExitStatus::success__meta)?;
86    module.function_meta(ExitStatus::display_fmt__meta)?;
87    module.function_meta(ExitStatus::debug_fmt__meta)?;
88
89    module.ty::<Output>()?;
90    module.function_meta(Output::debug_fmt__meta)?;
91
92    module.ty::<Stdio>()?;
93    module.function_meta(Stdio::null__meta)?;
94    module.function_meta(Stdio::inherit__meta)?;
95    module.function_meta(Stdio::piped__meta)?;
96    module.function_meta(Stdio::debug_fmt__meta)?;
97
98    module.ty::<ChildStdin>()?;
99    module.function_meta(ChildStdin::debug_fmt__meta)?;
100    module.function_meta(ChildStdin::try_into_stdio__meta)?;
101
102    module.ty::<ChildStdout>()?;
103    module.function_meta(ChildStdout::debug_fmt__meta)?;
104    module.function_meta(ChildStdout::try_into_stdio__meta)?;
105
106    module.ty::<ChildStderr>()?;
107    module.function_meta(ChildStderr::debug_fmt__meta)?;
108    module.function_meta(ChildStderr::try_into_stdio__meta)?;
109
110    Ok(module)
111}
112
113/// This structure mimics the API of [`std::process::Command`] found in the
114/// standard library, but replaces functions that create a process with an
115/// asynchronous variant. The main provided asynchronous functions are
116/// [spawn](Command::spawn), [status](Command::status), and
117/// [output](Command::output).
118///
119/// `Command` uses asynchronous versions of some `std` types (for example
120/// [`Child`]).
121///
122/// [`std::process::Command`]:
123///     https://doc.rust-lang.org/std/process/struct.Command.html
124/// [`Child`]: struct@Child
125#[derive(Debug, Any)]
126#[rune(item = ::process)]
127struct Command {
128    inner: process::Command,
129}
130
131impl Command {
132    /// Constructs a new `Command` for launching the program at path `program`,
133    /// with the following default configuration:
134    ///
135    /// * No arguments to the program
136    /// * Inherit the current process's environment
137    /// * Inherit the current process's working directory
138    /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes
139    ///   for `output`
140    ///
141    /// Builder methods are provided to change these defaults and otherwise
142    /// configure the process.
143    ///
144    /// If `program` is not an absolute path, the `PATH` will be searched in an
145    /// OS-defined way.
146    ///
147    /// The search path to be used may be controlled by setting the `PATH`
148    /// environment variable on the Command, but this has some implementation
149    /// limitations on Windows (see issue [rust-lang/rust#37519]).
150    ///
151    /// # Examples
152    ///
153    /// Basic usage:
154    ///
155    /// ```rune,no_run
156    /// use process::Command;
157    /// let command = Command::new("sh");
158    /// ```
159    ///
160    /// [rust-lang/rust#37519]: https://github.com/rust-lang/rust/issues/37519
161    #[rune::function(keep, path = Self::new)]
162    fn new(command: &str) -> Self {
163        Self {
164            inner: process::Command::new(command),
165        }
166    }
167
168    /// Adds an argument to pass to the program.
169    ///
170    /// Only one argument can be passed per use. So instead of:
171    ///
172    /// ```rune,no_run
173    /// use process::Command;
174    ///
175    /// let command = Command::new("sh");
176    /// command.arg("-C /path/to/repo");
177    /// ```
178    ///
179    /// usage would be:
180    ///
181    /// ```rune,no_run
182    /// use process::Command;
183    ///
184    /// let command = Command::new("sh");
185    /// command.arg("-C");
186    /// command.arg("/path/to/repo");
187    /// ```
188    ///
189    /// To pass multiple arguments see [`args`].
190    ///
191    /// [`args`]: method@Self::args
192    ///
193    /// # Examples
194    ///
195    /// Basic usage:
196    ///
197    /// ```rune,no_run
198    /// use process::Command;
199    ///
200    /// let command = Command::new("ls");
201    /// command.arg("-l");
202    /// command.arg("-a");
203    ///
204    /// let output = command.output().await?;
205    /// ```
206    #[rune::function(keep, instance)]
207    fn arg(&mut self, arg: &str) {
208        self.inner.arg(arg);
209    }
210
211    /// Adds multiple arguments to pass to the program.
212    ///
213    /// To pass a single argument see [`arg`].
214    ///
215    /// [`arg`]: method@Self::arg
216    ///
217    /// # Examples
218    ///
219    /// Basic usage:
220    ///
221    /// ```rune,no_run
222    /// use process::Command;
223    ///
224    /// let command = Command::new("ls");
225    /// command.args(["-l", "-a"]);
226    ///
227    /// let output = command.output().await?;
228    /// ```
229    #[rune::function(keep, instance)]
230    fn args(&mut self, args: &[Value]) -> VmResult<()> {
231        for arg in args {
232            self.inner.arg(&*vm_try!(arg.borrow_string_ref()));
233        }
234
235        VmResult::Ok(())
236    }
237
238    /// Sets executable argument.
239    ///
240    /// Set the first process argument, `argv[0]`, to something other than the
241    /// default executable path.
242    #[cfg(unix)]
243    #[rune::function(keep, instance)]
244    fn arg0(&mut self, arg: &str) {
245        self.inner.arg0(arg);
246    }
247
248    /// Sets configuration for the child process's standard input (stdin)
249    /// handle.
250    ///
251    /// Defaults to [`inherit`].
252    ///
253    /// [`inherit`]: process::Stdio::inherit
254    ///
255    /// # Examples
256    ///
257    /// Basic usage:
258    ///
259    /// ```rune,no_run
260    /// use process::{Command, Stdio};
261    ///
262    /// let command = Command::new("ls");
263    /// command.stdin(Stdio::null());
264    ///
265    /// let output = command.output().await?;
266    /// ```
267    #[rune::function(keep, instance)]
268    fn stdin(&mut self, stdio: Stdio) {
269        self.inner.stdin(stdio.inner);
270    }
271
272    /// Sets configuration for the child process's standard output (stdout)
273    /// handle.
274    ///
275    /// Defaults to [`inherit`] when used with `spawn` or `status`, and defaults
276    /// to [`piped`] when used with `output`.
277    ///
278    /// [`inherit`]: process::Stdio::inherit
279    /// [`piped`]: process::Stdio::piped
280    ///
281    /// # Examples
282    ///
283    /// Basic usage:
284    ///
285    /// ```rune,no_run
286    /// use process::{Command, Stdio};
287    ///
288    /// let command = Command::new("ls");
289    /// command.stdout(Stdio::null());
290    ///
291    /// let output = command.output().await?;
292    /// ```
293    #[rune::function(keep, instance)]
294    fn stdout(&mut self, stdio: Stdio) {
295        self.inner.stdout(stdio.inner);
296    }
297
298    /// Sets configuration for the child process's standard error (stderr)
299    /// handle.
300    ///
301    /// Defaults to [`inherit`] when used with `spawn` or `status`, and defaults
302    /// to [`piped`] when used with `output`.
303    ///
304    /// [`inherit`]: process::Stdio::inherit
305    /// [`piped`]: process::Stdio::piped
306    ///
307    /// # Examples
308    ///
309    /// Basic usage:
310    ///
311    /// ```rune,no_run
312    /// use process::{Command, Stdio};
313    ///
314    /// let command = Command::new("ls");
315    /// command.stderr(Stdio::null());
316    ///
317    /// let output = command.output().await?;
318    /// ```
319    #[rune::function(keep, instance)]
320    fn stderr(&mut self, stdio: Stdio) {
321        self.inner.stderr(stdio.inner);
322    }
323
324    /// Controls whether a `kill` operation should be invoked on a spawned child
325    /// process when its corresponding `Child` handle is dropped.
326    ///
327    /// By default, this value is assumed to be `false`, meaning the next
328    /// spawned process will not be killed on drop, similar to the behavior of
329    /// the standard library.
330    ///
331    /// # Caveats
332    ///
333    /// On Unix platforms processes must be "reaped" by their parent process
334    /// after they have exited in order to release all OS resources. A child
335    /// process which has exited, but has not yet been reaped by its parent is
336    /// considered a "zombie" process. Such processes continue to count against
337    /// limits imposed by the system, and having too many zombie processes
338    /// present can prevent additional processes from being spawned.
339    ///
340    /// Although issuing a `kill` signal to the child process is a synchronous
341    /// operation, the resulting zombie process cannot be `.await`ed inside of
342    /// the destructor to avoid blocking other tasks. The tokio runtime will, on
343    /// a best-effort basis, attempt to reap and clean up such processes in the
344    /// background, but no additional guarantees are made with regard to how
345    /// quickly or how often this procedure will take place.
346    ///
347    /// If stronger guarantees are required, it is recommended to avoid dropping
348    /// a [`Child`] handle where possible, and instead utilize
349    /// `child.wait().await` or `child.kill().await` where possible.
350    #[rune::function(keep, instance)]
351    pub fn kill_on_drop(&mut self, kill_on_drop: bool) {
352        self.inner.kill_on_drop(kill_on_drop);
353    }
354
355    /// Executes the command as a child process, returning a handle to it.
356    ///
357    /// By default, stdin, stdout and stderr are inherited from the parent.
358    ///
359    /// This method will spawn the child process synchronously and return a
360    /// handle to a future-aware child process. The `Child` returned implements
361    /// `Future` itself to acquire the `ExitStatus` of the child, and otherwise
362    /// the `Child` has methods to acquire handles to the stdin, stdout, and
363    /// stderr streams.
364    ///
365    /// All I/O this child does will be associated with the current default
366    /// event loop.
367    ///
368    /// # Examples
369    ///
370    /// Basic usage:
371    ///
372    /// ```rune,no_run
373    /// use process::Command;
374    ///
375    /// async fn run_ls() {
376    ///     let command = Command::new("ls");
377    ///     command.spawn()?.wait().await?;
378    /// }
379    /// ```
380    ///
381    /// # Caveats
382    ///
383    /// ## Dropping/Cancellation
384    ///
385    /// Similar to the behavior to the standard library, and unlike the futures
386    /// paradigm of dropping-implies-cancellation, a spawned process will, by
387    /// default, continue to execute even after the `Child` handle has been
388    /// dropped.
389    ///
390    /// The [`Command::kill_on_drop`] method can be used to modify this behavior
391    /// and kill the child process if the `Child` wrapper is dropped before it
392    /// has exited.
393    ///
394    /// ## Unix Processes
395    ///
396    /// On Unix platforms processes must be "reaped" by their parent process
397    /// after they have exited in order to release all OS resources. A child
398    /// process which has exited, but has not yet been reaped by its parent is
399    /// considered a "zombie" process. Such processes continue to count against
400    /// limits imposed by the system, and having too many zombie processes
401    /// present can prevent additional processes from being spawned.
402    ///
403    /// The tokio runtime will, on a best-effort basis, attempt to reap and
404    /// clean up any process which it has spawned. No additional guarantees are
405    /// made with regard to how quickly or how often this procedure will take
406    /// place.
407    ///
408    /// It is recommended to avoid dropping a [`Child`] process handle before it
409    /// has been fully `await`ed if stricter cleanup guarantees are required.
410    ///
411    /// [`Command`]: crate::process::Command
412    /// [`Command::kill_on_drop`]: crate::process::Command::kill_on_drop
413    /// [`Child`]: crate::process::Child
414    ///
415    /// # Errors
416    ///
417    /// On Unix platforms this method will fail with
418    /// `std::io::ErrorKind::WouldBlock` if the system process limit is reached
419    /// (which includes other applications running on the system).
420    #[rune::function(keep, instance)]
421    fn spawn(&mut self) -> io::Result<Child> {
422        Ok(Child {
423            inner: self.inner.spawn()?,
424        })
425    }
426
427    #[rune::function(keep, protocol = DEBUG_FMT)]
428    fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
429        vm_write!(f, "{self:?}")
430    }
431}
432
433/// Representation of a child process spawned onto an event loop.
434///
435/// # Caveats
436///
437/// Similar to the behavior to the standard library, and unlike the futures
438/// paradigm of dropping-implies-cancellation, a spawned process will, by
439/// default, continue to execute even after the `Child` handle has been dropped.
440///
441/// The `Command::kill_on_drop` method can be used to modify this behavior and
442/// kill the child process if the `Child` wrapper is dropped before it has
443/// exited.
444#[derive(Debug, Any)]
445#[rune(item = ::process)]
446struct Child {
447    // we use an option to avoid a panic if we try to complete the child process
448    // multiple times.
449    inner: process::Child,
450}
451
452impl Child {
453    /// The handle for writing to the child's standard input (stdin), if it has
454    /// been captured. To avoid partially moving the `child` and thus blocking
455    /// yourself from calling functions on `child` while using `stdin`, you
456    /// might find it helpful to do:
457    ///
458    /// ```rune,no_run
459    /// # let child = #{};
460    /// let stdin = child.stdin()?;
461    /// ```
462    #[rune::function(keep, instance)]
463    fn stdin(&mut self) -> Option<ChildStdin> {
464        let inner = self.inner.stdin.take()?;
465        Some(ChildStdin { inner })
466    }
467
468    /// The handle for reading from the child's standard output (stdout), if it
469    /// has been captured. You might find it helpful to do
470    ///
471    /// ```rune,no_run
472    /// # let child = #{};
473    /// let stdout = child.stdout.take()?;
474    /// ```
475    ///
476    /// to avoid partially moving the `child` and thus blocking yourself from
477    /// calling functions on `child` while using `stdout`.
478    #[rune::function(keep, instance)]
479    fn stdout(&mut self) -> Option<ChildStdout> {
480        let inner = self.inner.stdout.take()?;
481        Some(ChildStdout { inner })
482    }
483
484    /// The handle for reading from the child's standard error (stderr), if it
485    /// has been captured. You might find it helpful to do
486    ///
487    /// ```rune,no_run
488    /// # let child = #{};
489    /// let stderr = child.stderr()?;
490    /// ```
491    ///
492    /// to avoid partially moving the `child` and thus blocking yourself from
493    /// calling functions on `child` while using `stderr`.
494    #[rune::function(keep, instance)]
495    fn stderr(&mut self) -> Option<ChildStderr> {
496        let inner = self.inner.stderr.take()?;
497        Some(ChildStderr { inner })
498    }
499
500    /// Returns the OS-assigned process identifier associated with this child
501    /// while it is still running.
502    ///
503    /// Once the child has been polled to completion this will return `None`.
504    /// This is done to avoid confusion on platforms like Unix where the OS
505    /// identifier could be reused once the process has completed.
506    #[rune::function(keep, instance)]
507    fn id(&self) -> Option<u32> {
508        self.inner.id()
509    }
510
511    /// Attempts to force the child to exit, but does not wait for the request
512    /// to take effect.
513    ///
514    /// On Unix platforms, this is the equivalent to sending a `SIGKILL`. Note
515    /// that on Unix platforms it is possible for a zombie process to remain
516    /// after a kill is sent; to avoid this, the caller should ensure that
517    /// either `child.wait().await` or `child.try_wait()` is invoked
518    /// successfully.
519    #[rune::function(keep, instance)]
520    fn start_kill(&mut self) -> io::Result<()> {
521        self.inner.start_kill()
522    }
523
524    /// Forces the child to exit.
525    ///
526    /// This is equivalent to sending a `SIGKILL` on unix platforms.
527    ///
528    /// If the child has to be killed remotely, it is possible to do it using a
529    /// combination of the select! macro and a `oneshot` channel. In the
530    /// following example, the child will run until completion unless a message
531    /// is sent on the `oneshot` channel. If that happens, the child is killed
532    /// immediately using the `.kill()` method.
533    ///
534    /// ```rune,no_run
535    /// use process::Command;
536    /// # async fn wait_for_something() {}
537    ///
538    /// let child = Command::new("sleep");
539    /// child.arg("1");
540    ///
541    /// let child = child.spawn();
542    ///
543    /// let recv = wait_for_something();
544    ///
545    /// select {
546    ///     _ = child.wait() => {}
547    ///     _ = recv => child.kill().await.expect("kill failed"),
548    /// }
549    /// ```
550    #[rune::function(keep, instance, path = Self::kill)]
551    async fn kill(mut this: Mut<Self>) -> io::Result<()> {
552        this.inner.kill().await
553    }
554
555    /// Waits for the child to exit completely, returning the status that it
556    /// exited with. This function will continue to have the same return value
557    /// after it has been called at least once.
558    ///
559    /// The stdin handle to the child process, if any, will be closed
560    /// before waiting. This helps avoid deadlock: it ensures that the
561    /// child does not block waiting for input from the parent, while
562    /// the parent waits for the child to exit.
563    ///
564    /// If the caller wishes to explicitly control when the child's stdin
565    /// handle is closed, they may `.take()` it before calling `.wait()`:
566    ///
567    /// # Cancel safety
568    ///
569    /// This function is cancel safe.
570    ///
571    /// ```rune,no_run
572    /// use process::{Command, Stdio};
573    ///
574    /// let child = Command::new("cat");
575    /// child.stdin(Stdio::piped());
576    ///
577    /// let child = child.spawn()?;
578    ///
579    /// let stdin = child.stdin()?;
580    ///
581    /// // wait for the process to complete
582    /// let _ = child.wait().await?;
583    /// ```
584    #[rune::function(keep, instance, path = Self::wait)]
585    async fn wait(mut this: Mut<Self>) -> io::Result<ExitStatus> {
586        let inner = this.inner.wait().await?;
587        Ok(ExitStatus { inner })
588    }
589
590    /// Returns a future that will resolve to an `Output`, containing the exit
591    /// status, stdout, and stderr of the child process.
592    ///
593    /// The returned future will simultaneously waits for the child to exit and
594    /// collect all remaining output on the stdout/stderr handles, returning an
595    /// `Output` instance.
596    ///
597    /// The stdin handle to the child process, if any, will be closed before
598    /// waiting. This helps avoid deadlock: it ensures that the child does not
599    /// block waiting for input from the parent, while the parent waits for the
600    /// child to exit.
601    ///
602    /// By default, stdin, stdout and stderr are inherited from the parent. In
603    /// order to capture the output into this `Output` it is necessary to create
604    /// new pipes between parent and child. Use `stdout(Stdio::piped())` or
605    /// `stderr(Stdio::piped())`, respectively, when creating a `Command`.
606    #[rune::function(keep, vm_result, instance)]
607    async fn wait_with_output(self) -> io::Result<Output> {
608        let output = self.inner.wait_with_output().await?;
609
610        Ok(Output {
611            status: ExitStatus {
612                inner: output.status,
613            },
614            stdout: Value::new(Bytes::from_vec(Vec::try_from(output.stdout).vm?)).vm?,
615            stderr: Value::new(Bytes::from_vec(Vec::try_from(output.stderr).vm?)).vm?,
616        })
617    }
618
619    #[rune::function(keep, protocol = DEBUG_FMT)]
620    fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
621        vm_write!(f, "{:?}", self.inner)
622    }
623}
624
625/// The output of a finished process.
626///
627/// This is returned in a Result by either the [`output`] method of a
628/// [`Command`], or the [`wait_with_output`] method of a [`Child`] process.
629///
630/// [`output`]: Command::output
631/// [`wait_with_output`]: Child::wait_with_output
632#[derive(Debug, Any)]
633#[rune(item = ::process)]
634struct Output {
635    /// The status (exit code) of the process.
636    #[rune(get, copy)]
637    status: ExitStatus,
638    /// The data that the process wrote to stdout.
639    #[rune(get)]
640    stdout: Value,
641    /// The data that the process wrote to stderr.
642    #[rune(get)]
643    stderr: Value,
644}
645
646impl Output {
647    #[rune::function(keep, protocol = DEBUG_FMT)]
648    fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
649        vm_write!(f, "{self:?}")
650    }
651}
652
653/// The exit status from a completed child process
654#[derive(Debug, TryClone, Clone, Copy, Any)]
655#[rune(item = ::process)]
656struct ExitStatus {
657    inner: std::process::ExitStatus,
658}
659
660impl ExitStatus {
661    /// Was termination successful? Signal termination is not considered a
662    /// success, and success is defined as a zero exit status.
663    ///
664    /// # Examples
665    ///
666    /// ```rune,no_run
667    /// use process::Command;
668    ///
669    /// let command = Command::new("mkdir");
670    /// command.arg("projects");
671    ///
672    /// let status = command.status()?;
673    ///
674    /// if status.success() {
675    ///     println!("'projects/' directory created");
676    /// } else {
677    ///     println!("failed to create 'projects/' directory: {status}");
678    /// }
679    /// ```
680    #[rune::function(keep)]
681    fn success(&self) -> bool {
682        self.inner.success()
683    }
684
685    /// Returns the exit code of the process, if any.
686    ///
687    /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
688    /// process finished by calling `exit`.  Note that on Unix the exit status is truncated to 8
689    /// bits, and that values that didn't come from a program's call to `exit` may be invented by the
690    /// runtime system (often, for example, 255, 254, 127 or 126).
691    ///
692    /// On Unix, this will return `None` if the process was terminated by a signal.
693    /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an
694    /// extension trait for extracting any such signal, and other details, from the `ExitStatus`.
695    ///
696    /// # Examples
697    ///
698    /// ```rune,no_run
699    /// use process::Command;
700    ///
701    /// let command = Command::new("mkdir");
702    /// command.arg("projects");
703    ///
704    /// let status = command.status().await?;
705    ///
706    /// match status.code() {
707    ///     Some(code) => println!("Exited with status code: {code}"),
708    ///     None => println!("Process terminated by signal")
709    /// }
710    /// ```
711    #[rune::function(keep)]
712    fn code(&self) -> Option<i32> {
713        self.inner.code()
714    }
715
716    #[rune::function(keep, protocol = DISPLAY_FMT)]
717    fn display_fmt(&self, f: &mut Formatter) -> VmResult<()> {
718        vm_write!(f, "{}", self.inner)
719    }
720
721    #[rune::function(keep, protocol = DEBUG_FMT)]
722    fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
723        vm_write!(f, "{:?}", self.inner)
724    }
725}
726
727/// Describes what to do with a standard I/O stream for a child process when passed to the stdin, stdout, and stderr methods of Command.
728#[derive(Debug, Any)]
729#[rune(item = ::process)]
730struct Stdio {
731    inner: std::process::Stdio,
732}
733
734impl Stdio {
735    /// This stream will be ignored. This is the equivalent of attaching the stream to /dev/null.
736    #[rune::function(keep, path = Self::null)]
737    fn null() -> Self {
738        Self {
739            inner: std::process::Stdio::null(),
740        }
741    }
742
743    /// The child inherits from the corresponding parent descriptor. This is the default.
744    #[rune::function(keep, path = Self::inherit)]
745    fn inherit() -> Self {
746        Self {
747            inner: std::process::Stdio::inherit(),
748        }
749    }
750
751    /// A new pipe should be arranged to connect the parent and child processes.
752    #[rune::function(keep, path = Self::piped)]
753    fn piped() -> Self {
754        Self {
755            inner: std::process::Stdio::piped(),
756        }
757    }
758
759    #[rune::function(keep, protocol = DEBUG_FMT)]
760    fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
761        vm_write!(f, "{:?}", self.inner)
762    }
763}
764
765macro_rules! stdio_stream {
766    ($name:ident, $stream:tt) => {
767        #[derive(Debug, Any)]
768        #[rune(item = ::process)]
769        #[doc = concat!("The ", $stream, " stream for spawned children.")]
770        struct $name {
771            inner: process::$name,
772        }
773
774        impl $name {
775            /// Try to convert into a `Stdio`, which allows creating a pipeline between processes.
776            ///
777            /// This consumes the stream, as it can only be used once.
778            ///
779            /// Returns a Result<Stdio>
780            #[rune::function(keep, instance)]
781            fn try_into_stdio(self) -> io::Result<Stdio> {
782                Ok(Stdio {
783                    inner: self.inner.try_into()?,
784                })
785            }
786
787            #[rune::function(keep, protocol = DEBUG_FMT)]
788            fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
789                vm_write!(f, "{:?}", self.inner)
790            }
791        }
792    };
793}
794stdio_stream!(ChildStdin, "stdin");
795stdio_stream!(ChildStdout, "stdout");
796stdio_stream!(ChildStderr, "stderr");