/*
 * Copyright (c) 2017 Jean Guyomarc'h
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

extern crate std;

use std::error::Error as FmtError;
use self::Error::*;

#[derive(Debug)]
pub enum Error {
   IOError(std::io::Error),
   Failed(i32),
   Killed,
}

impl From<i32> for Error {
   fn from(code: i32) -> Self {
      Failed(code)
   }
}

impl From<std::io::Error> for Error {
   fn from(error: std::io::Error) -> Self {
      IOError(error)
   }
}

impl std::fmt::Display for Error {
   fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
      match *self {
         Failed(ref code) => write!(f, "{}: {}", self.description(), code),
         _ => write!(f, "{}", self.description()),
      }
   }
}

impl std::error::Error for Error {
   fn description(&self) -> &str {
      match *self {
         IOError(ref err) => err.description(),
         Killed => "Process received a deadly signal",
         Failed(_) => "Process returned a non-zero exit code",
      }
   }
}

pub fn new(prog: &str) -> std::process::Command {
   let mut cmd = std::process::Command::new(prog);
   cmd.stdin(std::process::Stdio::null());
   cmd
}

pub fn run(cmd: &mut std::process::Command) -> Result<(), Error> {
   debug!("Running: {:?}", cmd);
   match cmd.spawn() {
      /* Process was created :) */
      Ok(ref mut child) => {
         match child.wait() {
            Ok(status) => {
               if status.success() { Ok(()) }
               else {
                  /* Crap... say that something went bad */
                  if let Some(code) = status.code() { Err(Failed(code)) }
                  else { Err(Killed) }
               }
            },
            /* Failed to run the process normally */
            Err(err) => { Err(IOError(err)) }
         }
      },
      /* Process could not be created */
      Err(err) => { Err(IOError(err)) }
   }
}

pub fn run_get_stdout(mut cmd: &mut std::process::Command) -> Result<String, Error> {
   cmd.stdout(std::process::Stdio::piped());
   try!(run(&mut cmd));
   let output = try!(cmd.output()).stdout;
   Ok(String::from_utf8(output).unwrap())
}
