runcmd/
lib.rs

1/*!
2# RunCmd
3
4This library is used for extending `Execute` which is extending `Command` in order to execute commands more easily.  Especially made for simple shell commands returning an exit code as a number, stdout and stderr as strings.
5
6## Usage
7
8```rust
9use std::process::Command;
10
11use runcmd::RunCmd;
12
13RunCmd::new("echo \"Hello World\"").execute();
14
15```
16
17### verbose()
18Will print the ins and outs to stdout
19
20```rust
21RunCmd::new("echo \"Hello World\"")
22    .verbose()
23    .execute();
24```
25
26### shell()
27
28Sets the executor to run the command in a shell using the underlying Execute::shell rather than Execute::command.
29
30```rust
31RunCmd::new("echo \"Hello World\"")
32    .shell()
33    .execute();
34```
35
36### executep()
37
38Runs the command, without returning anything, but panics if the command doesn't succeed.  Useful in only the most trival circumstances.
39
40```rust
41RunCmd::new("echo \"Hello World\"")
42    .shell()
43    .executep();
44```
45
46### execute()
47
48Runs the command, returning a RunCmdOutput.
49
50```rust
51let retval: RunCmdOutput = RunCmd::new("echo \"Hello World\"").execute();
52```
53
54It returns the following.
55```rust
56pub struct RunCmdOutput {
57    pub cmd: String,
58    pub stdout: String,
59    pub stderr: String,
60    pub exitcode: i32
61}
62```
63*/
64
65extern crate execute;
66
67use std::process::Stdio;
68
69use execute::{Execute, command, shell};
70
71#[derive(Clone)]
72pub struct RunCmdOutput {
73    pub cmd: String,
74    pub stdout: String,
75    pub stderr: String,
76    pub exitcode: i32
77}
78
79pub struct RunCmd {
80    retval: RunCmdOutput,
81    verbose: bool,
82    execute: bool,
83    shell: bool
84}
85
86impl RunCmd {
87
88    pub fn new(cmd: &str) -> RunCmd {
89        RunCmd {
90            retval: RunCmdOutput { 
91                cmd: String::from(cmd),
92                stdout: String::from(""),
93                stderr: String::from(""),
94                exitcode: 0
95              },
96            execute: false,
97            verbose: false,
98            shell: false
99        }
100    }
101
102    /// Explicitly prints out stdout, stderr, and the exit code for the command run.
103    /// But it disables real time output
104    #[allow(dead_code)]
105    pub fn verbose(&mut self) -> &mut RunCmd {
106        self.verbose = true;
107        self
108    }
109
110    /// Forces the command to run in a system shell.  Can fix some issue with complex commands.
111    #[allow(dead_code)]
112    pub fn shell(&mut self) -> &mut RunCmd {
113        self.shell = true;
114        self
115    }
116
117    fn print(&self) {
118        println!("cmd:\n '{}'\n", self.retval.cmd);
119        println!("stdout:\n '{}'\n", self.retval.stdout);
120        println!("stderr:\n '{}'\n", self.retval.stderr);
121        println!("exitcode: '{}'\n\n", self.retval.exitcode);
122    }
123
124    /// Standard execution.  If it doesn't succeed it will just panic.
125    pub fn executep(&mut self) {
126        self.execute = true;
127
128        let retval = self.execute();
129
130        if retval.exitcode != 0 {
131            if self.verbose {
132                self.print();
133            }
134            panic!("Exitcode != 0")
135        }
136    }
137
138    /// Execution returning a structure with the output: exitcode, stdout, stderr.
139    pub fn execute(&mut self) -> RunCmdOutput {
140        let mut executor;
141
142        if self.shell {
143            executor = shell(&self.retval.cmd)
144        } else {
145            executor = command(&self.retval.cmd)
146        }
147
148        if self.verbose || !self.execute {
149            executor.stdout(Stdio::piped());
150            executor.stderr(Stdio::piped());
151        }
152
153        let output = executor.execute_output().unwrap();
154
155        if let Some(exit_code) = output.status.code() {
156            self.retval.exitcode = exit_code;
157            self.retval.stdout =  String::from_utf8(output.stdout).unwrap();
158            self.retval.stderr =  String::from_utf8(output.stderr).unwrap();
159        } else {
160            self.retval.exitcode = -1;
161            self.retval.stderr =  String::from("Interrupted! in RunCmd");
162        }
163
164        if self.verbose {
165            self.print();
166        }
167
168        return self.retval.clone()
169    }
170
171}
172
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn execute_pass() {
180        RunCmd::new("bash -c \"exit 0\"").executep();
181    }
182
183    #[test]
184    #[should_panic]
185    fn execute_fail() {
186        RunCmd::new("bash -c \"exit -1\"").executep();
187    }
188
189    #[test]
190    fn execute_verbose() {
191        RunCmd::new("echo bar; exit 0")
192            .verbose()
193            .execute();
194    }
195
196    #[test]
197    fn execute_shell() {
198        RunCmd::new("echo foobar; exit 0").shell().execute();
199    }
200
201    #[test]
202    fn execute_output_pass() {
203        let retval = RunCmd::new("bash -c \"echo foo; >&2 echo bar; exit -1\"").execute();
204        assert_eq!(retval.exitcode, 255);
205        assert_eq!(&retval.stdout, "foo\n");
206        assert_eq!(&retval.stderr, "bar\n");
207        assert_eq!(&retval.cmd, "bash -c \"echo foo; >&2 echo bar; exit -1\"");
208    }
209
210    #[test]
211    fn execute_output_shell_pass() {
212        let retval = RunCmd::new("echo foo; >&2 echo bar; exit -1").shell().execute();
213        assert_eq!(retval.exitcode, 255);
214        assert_eq!(&retval.stdout, "foo\n");
215        assert_eq!(&retval.stderr, "bar\n");
216        assert_eq!(&retval.cmd, "echo foo; >&2 echo bar; exit -1");
217    }
218
219}