1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::io::BufReader;
use std::io::prelude::*;
use std::process::Child;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

/// represents a single line of output from a child process
#[derive(Debug)]
pub enum Line {
  /// a line of stdout output
  StdOut(String),
  /// a line of stderr output
  StdErr(String)
}

/// represents a source of child process line output
pub struct Lines {
  rx: Receiver<Option<Line>>,
}

/// represents an iterable of Line instances
pub struct Iter<'a> {
  lines: &'a Lines
}

impl Lines {
  pub fn iter(&self) -> Iter {
    Iter { lines: self }
  }
}

impl<'a> Iterator for Iter<'a> {
  type Item = Line;
  fn next(&mut self) -> Option<Line> {
    match self.lines.rx.recv() {
      Ok(line) => line,
            _  => None,
    }
  }
}

/// creates a new Lines instance
///
/// ```rust
/// use std::process::{Command, Stdio};
///  match Command::new("...")
///      .stdout(Stdio::piped())
///      .stderr(Stdio::piped()).spawn() {
///          Ok(mut child) => {
///              let lines = pine::lines(&mut child);
///              child.wait().unwrap();
///              for l in lines.iter() {
///                  println!("{:?}", l)
///              }
///          },
///         _ => println!("failed to launch process")
///     }
/// ```
pub fn lines(child: &mut Child) -> Lines {
  let (tx, rx) = channel();
  fn read<R, F>(
    readable: Option<R>,
    tx: Sender<Option<Line>>,
    wrap: F
  ) where
    R: Send + 'static + Read,
    F: Send + 'static + Fn(String) -> Line {
    if let Some(r) = readable {
      thread::spawn(move || {
        let mut buf = BufReader::new(r);
        loop {
          let mut line = String::new();
            match buf.read_line(&mut line) {
                Ok(0) | Err(_)  => break,
                Ok(_)  => {
                    let _ = tx.send(Some(wrap(line)));
                }
          }
        }
      });
    } else {
      let _ = tx.send(None);
    }
  };
  read(child.stdout.take(), tx.clone(), |l| Line::StdOut(l));
  read(child.stderr.take(), tx.clone(), |l| Line::StdErr(l));
  Lines { rx: rx }
}

#[test]
fn it_works() {
}