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");