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,
})
}
}
|