/src/helpers/shell_pipe.rs

https://github.com/killercup/trpl-ebook · Rust · 69 lines · 56 code · 12 blank · 1 comment · 10 complexity · 8761c43858a1d0e1c9830774dabfe732 MD5 · raw file

  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io::prelude::*;
  4. use std::process::{self, Command, Stdio};
  5. #[derive(Debug, Hash, PartialEq, Eq)]
  6. enum CommandError {
  7. StdIn,
  8. StdOut,
  9. }
  10. impl fmt::Display for CommandError {
  11. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  12. match *self {
  13. CommandError::StdIn => write!(f, "Error getting stdin"),
  14. CommandError::StdOut => write!(f, "Error getting stdout"),
  15. }
  16. }
  17. }
  18. impl Error for CommandError {
  19. fn description(&self) -> &str {
  20. match *self {
  21. CommandError::StdIn => "Error getting stdin",
  22. CommandError::StdOut => "Error getting stdout",
  23. }
  24. }
  25. }
  26. pub fn run(command: &str, args: &str, input: &str) -> Result<String, Box<Error>> {
  27. let args: Vec<&str> = if args.is_empty() {
  28. vec![]
  29. } else {
  30. // Command arguments are space separated but may contain sub strings in quotation marks
  31. let mut in_substr = false;
  32. args.split(|c: char| {
  33. if c == '\'' { in_substr = !in_substr; }
  34. !in_substr && (c == ' ')
  35. }).collect()
  36. };
  37. let process = try!(
  38. Command::new(command)
  39. .args(&args)
  40. .stdin(Stdio::piped())
  41. .stdout(Stdio::piped())
  42. .spawn()
  43. );
  44. {
  45. let mut stdin: process::ChildStdin = try!(process.stdin.ok_or(CommandError::StdIn));
  46. try!(stdin.write_all(input.as_bytes()));
  47. }
  48. let mut output = String::new();
  49. let mut stdout: process::ChildStdout = try!(process.stdout.ok_or(CommandError::StdOut));
  50. try!(stdout.read_to_string(&mut output));
  51. Ok(output)
  52. }
  53. #[test]
  54. fn dry_run() {
  55. let output = run("cat", "", "lol").unwrap();
  56. assert_eq!(output, "lol");
  57. }