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
128
129
130
131
132
133
134
135
136
/*!
 pargs - command line argument parser

 the design goal of pargs is to simply return parsed arguments
 to a caller in a defined format for ease of lookup.

 pargs works with three common types of arguments:
 commands, flags and options.

 # Using pargs
 using pargs is very simple:
 define all three types of arguments that your program needs
 and pass them as individual `Vec<String>` to `pargs::parse()`.
 `parse()` will return a `HashMap` of the parsed arguments
  keyed by category so that your application can easily
 interpret them.

 # Definitions
 - `command_args` are defined as single arguments that do not have an assigned value
 - `command_args` args should be entered without a dash
 - `flag_args` are intended to be boolean values
 - `flag_args` should not be assigned a value - if they exist, they are interpreted as `true`
 - `option_args` should be assigned a value
 - `option_args` should be denoted with a `-` character
 - `option_args` can be assigned a value via `=` or space between arg and value

 # Example

 The following example shows a simple program that defines all three types of arguments
 (commands, flag and option). Pargs is passed a `Vec<String>` from `env::args()`
 at which point it parses the arguments and returns them to the program in a `HashMap<String,
 Vec<String>>` data structure.

 ```
 use std::env;

 fn main() {
    let actual_args: Vec<String> = env::args().collect();
    let command_args = vec![String::from("cool_command")];
    let flag_args = vec![String::from("-h")];
    let option_args = vec![String::from("-j"), String::from("-i")];

    let parsed_args = pargs::parse(actual_args, command_args, flag_args, option_args);

    match parsed_args {
        Ok(parsed_args) => println!("{:?}", parsed_args),
        Err(parsed_args) => println!("{}", parsed_args),
    }
 }
 ```

If we run this program with `cargo run cool_command -h -j=test123 -i=test456`,
 the output will be `{"flag_args": ["-h"], "command_args": ["cool_command"], "option_args": ["-j", "test123", "-i", "test456"]}`.

From here, we can lookup the values using `HashMap`'s methods and utilize them in our program.
*/

use std::collections::HashMap;
use std::io::{Error, ErrorKind};

#[cfg(test)]
mod tests;
/// parses arguments in relation to expected optional and required arguments
pub fn parse(
    actual_args: Vec<String>,
    command_args: Vec<String>,
    flag_args: Vec<String>,
    option_args: Vec<String>,
) -> Result<HashMap<String, Vec<String>>, Error> {
    let mut matches: HashMap<String, Vec<String>> = HashMap::new();

    // return Error if no required arguments are provided
    if actual_args.is_empty() {
        return Err(Error::new(
            ErrorKind::InvalidInput,
            "no arguments were provided.",
        ));
    }

    // make a clone of actual_args vec for index lookup
    let actual_args_ref = actual_args.clone();

    // iterate over actual_args and match them with each arg category
    for (i, arg) in actual_args.iter().enumerate() {
        // parse option_args that use `=` into key value pairs
        let str_vec: Vec<&str> = match arg.contains("=") {
            true => arg.split("=").collect(),
            false => vec![&arg],
        };

        let split_key: String = match str_vec.len() {
            1 => "".to_string(),
            _ => str_vec[0].to_string(),
        };

        let split_value: String = match str_vec.len() {
            1 => "".to_string(),
            _ => str_vec[1].to_string(),
        };

        // insert args into relative `HashMap` key
        if command_args.contains(&arg) {
            matches
                .entry("command_args".to_string())
                .or_insert(Vec::new())
                .push(arg.to_string())
        } else if flag_args.contains(&arg) {
            matches
                .entry("flag_args".to_string())
                .or_insert(Vec::new())
                .push(arg.to_string())
        } else if option_args.contains(&split_key) {
            matches
                .entry("option_args".to_string())
                .or_insert(Vec::new())
                .push(split_key);

            matches
                .entry("option_args".to_string())
                .or_insert(Vec::new())
                .push(split_value);
        } else if option_args.contains(&arg) {
            // parse option args that use a ` ` into key value pairs
            matches
                .entry("option_args".to_string())
                .or_insert(Vec::new())
                .push(arg.to_string());
            matches
                .entry("option_args".to_string())
                .or_insert(Vec::new())
                .push(actual_args_ref[i + 1].to_string());
        }
    }

    Ok(matches)
}