1
use std::{
2
    cell::RefCell,
3
    process::{Child, ChildStdin, ChildStdout, Command, Stdio},
4
};
5

            
6
use anyhow::{anyhow, Result};
7

            
8
use 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)]
14
pub 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

            
20
impl Builder {
21
    /// Create a builder that spawns a process based on the command being
22
    /// passed.
23
    #[must_use]
24
2
    pub fn new(command: Command) -> Self {
25
2
        Self { command: command.into() }
26
2
    }
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
1
    pub fn spawn(&self) -> Result<ChildBuilder> {
35
1
        let child =
36
1
            self.command.borrow_mut().stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
37
1
        Ok(ChildBuilder { child: child.into() })
38
1
    }
39
}
40

            
41
impl RwBuilder for Builder {
42
    type Reader = ChildStdout;
43
    type Writer = ChildStdin;
44

            
45
1
    fn reader(&self) -> Result<Self::Reader> {
46
1
        let mut child = self.command.borrow_mut().stdout(Stdio::piped()).spawn()?;
47
1
        child.stdout.take().ok_or_else(|| anyhow!("no child stdout"))
48
1
    }
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)]
60
pub struct ChildBuilder {
61
    /// Handle to the child process
62
    child: RefCell<Child>,
63
}
64

            
65
impl RwBuilder for ChildBuilder {
66
    type Reader = ChildStdout;
67
    type Writer = ChildStdin;
68

            
69
1
    fn reader(&self) -> Result<Self::Reader> {
70
1
        self.child
71
1
            .borrow_mut()
72
1
            .stdout
73
1
            .take()
74
1
            .ok_or_else(|| anyhow!("No child stdout. Did you already build a reader?"))
75
1
    }
76

            
77
1
    fn writer(&self) -> Result<Self::Writer> {
78
1
        self.child
79
1
            .borrow_mut()
80
1
            .stdin
81
1
            .take()
82
1
            .ok_or_else(|| anyhow!("No child stdin. Did you already build a writer?"))
83
1
    }
84
}