1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::{get_from, merge_envs, set, split_line, Dir};
use clap::{App, Arg, ArgMatches};
use std::collections::HashMap;
use std::io;

pub fn denver_arg<'a, 'b>() -> App<'a, 'b> {
    App::new("denver")
        .version("0.1")
        .author("Sam Raker <sam.raker@gmail.com>")
        .about("dotenv-like")
        .arg(
            Arg::with_name("env")
                .short("e")
                .long("env")
                .value_name("ENV")
                .help("Environment to use")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            Arg::with_name("set")
                .short("s")
                .long("set")
                .value_name("KEY=VALUE")
                .help("Set one-time vars (clobbers -f)")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            Arg::with_name("merge_left")
                .short("l")
                .long("merge_left")
                .help("Merge left, preserving earlier values")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("from")
                .short("f")
                .long("from")
                .value_name("KEY=ENV")
                .help("Set a variable from a specific environment")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            Arg::with_name("cmd")
                .short("c")
                .long("cmd")
                .value_name("CMD")
                .help("Command to run")
                .takes_value(true)
                .required(true)
                .index(1),
        )
}

pub fn get_args<'a>(
    matches: &'a ArgMatches,
) -> io::Result<(Vec<&'a str>, HashMap<String, String>)> {
    let cmd = get_cmd(matches);
    let ens = get_ens(matches);
    let froms = get_froms(matches)?;
    let sets = get_sets(matches);
    let dir = get_dir(matches);
    let mut e = merge_envs(ens, dir)?;
    set_froms(froms, &mut e)?;
    set_sets(sets, &mut e);
    Ok((cmd, e))
}

fn get_cmd<'a>(matches: &'a ArgMatches<'a>) -> Vec<&'a str> {
    matches
        .value_of("cmd")
        .unwrap()
        .split_whitespace()
        .collect()
}

fn get_ens<'a>(matches: &'a ArgMatches<'a>) -> Vec<&'a str> {
    matches.values_of("env").map_or(Vec::new(), |v| v.collect())
}

fn get_froms<'a>(matches: &'a ArgMatches<'a>) -> io::Result<Vec<Option<(String, String)>>> {
    let fs = matches.values_of("from").map_or(Vec::new(), |v| {
        v.map(|v| split_line(v.to_string())).collect()
    });
    fs.into_iter()
        .filter(|v| v.is_some())
        .map(|v| v.unwrap())
        .map(|(k, v)| get_from(v, k))
        .collect()
}

fn set_froms(
    froms: Vec<Option<(String, String)>>,
    mut e: &mut HashMap<String, String>,
) -> io::Result<()> {
    for (k, v) in froms.into_iter().flatten() {
        set(&mut e, k, v)
    }
    Ok(())
}

fn get_sets(matches: &ArgMatches) -> Vec<Option<(String, String)>> {
    matches
        .values_of("set")
        .map_or(Vec::new(), |v| {
            v.map(|v| split_line(v.to_string())).collect()
        })
        .into_iter()
        .filter(|v| v.is_some())
        .collect()
}

fn set_sets(sets: Vec<Option<(String, String)>>, mut e: &mut HashMap<String, String>) {
    for (k, v) in sets.into_iter().flatten() {
        set(&mut e, k, v);
    }
}

fn get_dir(matches: &ArgMatches) -> Dir {
    if matches.is_present("merge_left") {
        Dir::L
    } else {
        Dir::R
    }
}