1use std::process::{Command, Child, Stdio};
3use std::sync::{mpsc};
5use std::io::{Read};
6use std::str;
7
8pub struct Process {
9 pub child: Option<Child>,
10 pub rx_line: Option<mpsc::Receiver<Option<(bool, String)>>>,
11}
12
13impl Process {
14
15 pub fn start(cmd: &str, args: &[&str], current_dir: &str, env: &[(&str, &str)]) -> Result<Process, std::io::Error> {
16 fn create_process(cmd: &str, args: &[&str], current_dir: &str, env: &[(&str, &str)]) -> Result<Child, std::io::Error> {
17 let mut cbuild = if cmd.find("/").is_some() {
18 Command::new(&format!("{}/{}", current_dir, cmd))
19 }
20 else {
21 Command::new(cmd)
22 };
23 cbuild.args(args)
24 .stdin(Stdio::null())
25 .stdout(Stdio::piped())
26 .stderr(Stdio::piped())
27 .current_dir(current_dir);
28 for (key, value) in env {
29 cbuild.env(key, value);
30 }
31 cbuild.spawn()
32 }
33
34 let mut child = create_process(cmd, args, current_dir, env) ?;
35
36 let (tx_line, rx_line) = mpsc::channel();
37 let tx_err = tx_line.clone();
38 let mut stdout = child.stdout.take().expect("stdout cannot be taken!");
39 let mut stderr = child.stderr.take().expect("stderr cannot be taken!");
40
41 let _stdout_thread = {
42 std::thread::spawn(move || {
43 let mut storage = Vec::new();
44 loop {
45 let offset = storage.len();
46 storage.resize(offset + 1024, 0u8);
47 let new_len = storage.len();
48 let n_bytes_read = stdout.read(&mut storage[offset..new_len]).expect("cannot read");
49 if n_bytes_read == 0 {
50 tx_line.send(None).expect("tx_line cannot send - unexpected");
51 return;
52 }
53 storage.resize(offset + n_bytes_read, 0u8);
54 let mut start = 0;
55 for (index, ch) in storage.iter().enumerate() {
56 if *ch == '\n' as u8 {
57 if let Ok(line) = str::from_utf8(&storage[start..(index + 1)]) {
59 tx_line.send(Some((false, line.to_string()))).expect("tx_line cannot send - unexpected");
60 }
61 start = index + 1;
62 }
63 }
64 storage.drain(0..start);
65 }
66 })
67 };
68
69 let _stderr_thread = {
70 std::thread::spawn(move || {
71 let mut storage = Vec::new();
72 loop {
73 let offset = storage.len();
74 storage.resize(offset + 1024, 0u8);
75 let new_len = storage.len();
76 let n_bytes_read = stderr.read(&mut storage[offset..new_len]).expect("cannot read");
77 if n_bytes_read == 0 {
78 return;
79 }
80 storage.resize(offset + n_bytes_read, 0u8);
81 let mut start = 0;
82 for (index, ch) in storage.iter().enumerate() {
83 if *ch == '\n' as u8 {
84 if let Ok(line) = str::from_utf8(&storage[start..(index + 1)]) {
86 tx_err.send(Some((true, line.to_string()))).expect("tx_err cannot send - unexpected");
87 }
88 start = index + 1;
89 }
90 }
91 storage.drain(0..start);
92 }
93 })
94 };
95
96 Ok(Process {
97 child: Some(child),
98 rx_line: Some(rx_line),
99 })
100 }
101
102 pub fn wait(&mut self) {
103 if let Some(child) = &mut self.child {
104 let _ = child.wait();
105 self.child = None;
106 }
107 }
108
109 pub fn kill(&mut self) {
110 if let Some(child) = &mut self.child {
111 let _ = child.kill();
112 let _ = child.wait();
113 self.child = None;
114 }
115 }
116}