aboutsummaryrefslogtreecommitdiff
path: root/src/args.rs
blob: 5e99dc5f357071c96cd80c2c6e744d69c5cdb802 (plain)
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
use std::env;
use std::fmt;
use std::process;

const UNIQUE_DEFAULT: bool = false;
const N_SENTENCES_DEFAULT: u32 = 1;
pub const HELP_STR: &str = "Usage: spout [OPTIONS] <DIRECTORY>

Arguments:
  <DIRECTORY>  Path to data directory

Options:
  -n, --sentences  Number of sentences to generate [default: 1]
      --unique     Don't re-use entries in data files (program will fail upon running out)
  -h, --help       Print help";

#[derive(Debug)]
pub enum ArgError {
    NoDirectory,
    UnknownArg(String),
    OutOfArgs(String),
    BadNumber(String),
    FlagReuse(String),
}

impl fmt::Display for ArgError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ArgError::NoDirectory =>
                write!(f, "no directory path provided"),
            ArgError::OutOfArgs(last) =>
                write!(f, "expected one or more arguments after '{}'", last),
            ArgError::UnknownArg(s) =>
                write!(f, "unknown argument: '{}'", s),
            ArgError::BadNumber(s) =>
                write!(f, "'{}' is not a valid number", s),
            ArgError::FlagReuse(s) =>
                write!(f, "multiple uses of the '{}' flag", s),
        }
    }
}

pub struct Args {
    pub directory: String,
    pub n_sentences: u32,
    pub unique: bool,
}

impl Args {
    pub fn parse() -> Result<Args, ArgError> {
        let arg_vec: Vec<String> = env::args().collect();
        let mut i = 1; //skip first arg (execution path)

        let mut opt_n_sentences: Option<u32> = None;
        let mut opt_unique: Option<bool> = None;
        let mut opt_directory: Option<String> = None;

        while i < arg_vec.len() {
            match arg_vec[i].as_str() {
                "-h" | "--help" => {
                    println!("{}", HELP_STR);
                    process::exit(0);
                },
                "-n" | "--sentences" => match opt_n_sentences {
                    Some(_) => {
                        return Err(ArgError::FlagReuse(arg_vec[i].to_string()));
                    },
                    None => {
                        if i + 1 < arg_vec.len() {
                            // manually converting the error because it
                            // gives more useful information than ParseIntError
                            let n = arg_vec[i+1].parse::<u32>()
                                .map_err(|_| ArgError::BadNumber(arg_vec[i+1].to_string()))?;
                            opt_n_sentences = Some(n);
                        } else {
                            return Err(ArgError::OutOfArgs(arg_vec[i].to_string()));
                        }
                        i += 2;
                    },
                },
                "--unique" => match opt_unique {
                    Some(_) => {
                        return Err(ArgError::FlagReuse(arg_vec[i].to_string()));
                    },
                    None => {
                        opt_unique = Some(true);
                        i += 1;
                    },
                },
                other => {
                    opt_directory = match opt_directory {
                        Some(_) => {
                            return Err(ArgError::UnknownArg(other.to_string()));
                        },
                        None => Some(other.to_string()),
                    };
                    i += 1;
                },
            }
        }

        let directory = opt_directory.ok_or(ArgError::NoDirectory)?;
        let n_sentences = opt_n_sentences.unwrap_or(N_SENTENCES_DEFAULT);
        let unique = opt_unique.unwrap_or(UNIQUE_DEFAULT);

        Ok(Args {
            directory,
            n_sentences,
            unique,
        })
    }
}