rw_builder/
process.rs

1use std::{
2    cell::RefCell,
3    process::{Child, ChildStdin, ChildStdout, Command, Stdio},
4};
5
6use anyhow::{anyhow, Result};
7
8use crate::RwBuilder;
9
10/// Type for building readers and writers on top of a process handle.
11/// It is itself an `RwBuilder`, but can't be created through one.
12/// This is why we call it a source.
13#[derive(Debug)]
14pub struct Builder {
15    /// The command used to spawn the process to attach a reader and/or writer
16    /// to.
17    command: RefCell<Command>,
18}
19
20impl Builder {
21    /// Create a builder that spawns a process based on the command being
22    /// passed.
23    #[must_use]
24    pub fn new(command: Command) -> Self {
25        Self { command: command.into() }
26    }
27
28    /// Spawn a child process based on the command and return the actual builder
29    /// of the reader and writer. # Errors
30    /// In case spawning the child process fails the reason why is return as an
31    /// error.
32    /// # Errors
33    /// Propagates the error of failing to spawn a child process
34    pub fn spawn(&self) -> Result<ChildBuilder> {
35        let child =
36            self.command.borrow_mut().stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
37        Ok(ChildBuilder { child: child.into() })
38    }
39}
40
41impl RwBuilder for Builder {
42    type Reader = ChildStdout;
43    type Writer = ChildStdin;
44
45    fn reader(&self) -> Result<Self::Reader> {
46        let mut child = self.command.borrow_mut().stdout(Stdio::piped()).spawn()?;
47        child.stdout.take().ok_or_else(|| anyhow!("no child stdout"))
48    }
49
50    fn writer(&self) -> Result<Self::Writer> {
51        let mut child = self.command.borrow_mut().stdin(Stdio::piped()).spawn()?;
52        child.stdin.take().ok_or_else(|| anyhow!("no child stdin"))
53    }
54}
55
56/// Type for building readers and writers on top of a spawned child process.
57/// It is itself an `RwBuilder`, so it can be chained further.
58/// It is also a source.
59#[derive(Debug)]
60pub struct ChildBuilder {
61    /// Handle to the child process
62    child: RefCell<Child>,
63}
64
65impl RwBuilder for ChildBuilder {
66    type Reader = ChildStdout;
67    type Writer = ChildStdin;
68
69    fn reader(&self) -> Result<Self::Reader> {
70        self.child
71            .borrow_mut()
72            .stdout
73            .take()
74            .ok_or_else(|| anyhow!("No child stdout. Did you already build a reader?"))
75    }
76
77    fn writer(&self) -> Result<Self::Writer> {
78        self.child
79            .borrow_mut()
80            .stdin
81            .take()
82            .ok_or_else(|| anyhow!("No child stdin. Did you already build a writer?"))
83    }
84}