extern crate clap;
extern crate dotenv;

use clap::{App, AppSettings, Arg};
use std::os::unix::process::CommandExt;
use std::process::{Command, exit};

macro_rules! die {
    ($fmt:expr) => ({
        eprintln!($fmt);
        exit(1);
    });
    ($fmt:expr, $($arg:tt)*) => ({
        eprintln!($fmt, $($arg)*);
        exit(1);
    });
}

fn make_command(name: &str, args: Vec<&str>) -> Command {
    let mut command = Command::new(name);

    for arg in args {
        command.arg(arg);
    }

    return command;
}

fn main() {
    let matches = App::new("dotenv")
        .about("Run a command using the environment in a .env file")
        .usage("dotenv <COMMAND> [ARGS]...")
        .setting(AppSettings::AllowExternalSubcommands)
        .setting(AppSettings::ArgRequiredElseHelp)
        .setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("FILE")
             .short("f")
             .long("file")
             .takes_value(true)
             .help("Use a specific .env file (defaults to .env)"))
        .get_matches();

    match matches.value_of("FILE") {
        None => dotenv::dotenv(),
        Some(file) => dotenv::from_filename(file),
    }.unwrap_or_else(|e| die!("error: failed to load environment: {}", e));

    let mut command = match matches.subcommand() {
        (name, Some(matches)) => {
            let args = matches.values_of("")
                .map(|v| v.collect())
                .unwrap_or(Vec::new());

            make_command(name, args)
        },
        _ => die!("error: missing required argument <COMMAND>"),
    };

    if cfg!(target_os = "windows") {
        match command.spawn().and_then(|mut child| child.wait()) {
            Ok(status) => exit(status.code().unwrap_or(1)),
            Err(error) => die!("fatal: {}", error),
        };
    } else {
        let error = command.exec();
        die!("fatal: {}", error);
    };
}
