Skip to main content

pistonite_cu/process/pio/
pipe.rs

1use std::process::Stdio;
2
3use tokio::process::{Child as TokioChild, Command as TokioCommand};
4
5use crate::{BoxedFuture, Context as _};
6
7use super::{ChildInConfig, ChildOutConfig, ChildOutTask};
8
9/// Pipe the child's output into another command's stdin.
10///
11/// # Example
12/// ```rust,no_run
13/// # use pistonite_cu as cu;
14/// use cu::pre::*;
15///
16/// # fn main() -> cu::Result<()> {
17/// let (hello, out) = cu::which("echo")?.command()
18///     .arg("Hello, world!")
19///     .stdout(cu::pio::pipe())
20///     .stdie_null()
21///     .spawn()?;
22///
23/// cu::which("rev")?.command()
24///     .stdin(out)
25///     .stdoe(cu::lv::I)
26///     .wait_nz()?;
27/// hello.wait_nz()?;
28/// # Ok(()) }
29/// ```
30#[inline(always)]
31pub fn pipe() -> Pipe {
32    Pipe
33}
34pub struct Pipe;
35impl ChildOutConfig for Pipe {
36    type Task = PipeTask;
37    type __Null = super::__OCNonNull;
38
39    fn configure_stdout(&mut self, command: &mut TokioCommand) {
40        command.stdout(std::process::Stdio::piped());
41    }
42
43    fn configure_stderr(&mut self, command: &mut TokioCommand) {
44        command.stderr(std::process::Stdio::piped());
45    }
46
47    fn take(
48        self,
49        child: &mut TokioChild,
50        _name: Option<&str>,
51        is_out: bool,
52    ) -> crate::Result<Self::Task> {
53        let stream = super::take_child_out(child, is_out)?;
54        // note: the falliable conversion is necessary right now
55        // since tokio does not yet have a way for us to take
56        // the pipe out before it's converted to tokio's ChildStdout/ChildStderr
57        // see https://github.com/tokio-rs/tokio/pull/7249#issuecomment-3146995525
58        // once Command::spawn_with is stabilized, we can use it
59        let x: Result<Stdio, _> = match stream {
60            Ok(s) => s.try_into(),
61            Err(s) => s.try_into(),
62        };
63        let x = x.context("failed to convert tokio pipe to std pipe")?;
64        Ok(PipeTask(x))
65    }
66}
67pub struct PipeTask(Stdio);
68impl ChildOutTask for PipeTask {
69    type Output = PipeOutput;
70    fn run(self) -> (Option<BoxedFuture<()>>, Self::Output) {
71        (None, PipeOutput(Some(self.0)))
72    }
73}
74
75/// The output of [`pipe`]. Can be piped into another command's [`stdin`](crate::Command::stdin).
76pub struct PipeOutput(Option<Stdio>); // this uses an option right now to avoid dealing with unsafe
77impl ChildInConfig for PipeOutput {
78    type Task = ();
79
80    fn configure_stdin(&mut self, command: &mut TokioCommand) -> crate::Result<()> {
81        match self.0.take() {
82            Some(x) => {
83                command.stdin(x);
84                Ok(())
85            }
86            _ => crate::bail!("unexpected: pipe was already taken"),
87        }
88    }
89
90    fn take(self, _: &mut TokioChild) -> crate::Result<Self::Task> {
91        Ok(())
92    }
93}