-
Notifications
You must be signed in to change notification settings - Fork 2
/
docopt.rs
189 lines (149 loc) · 5.46 KB
/
docopt.rs
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
use cmp::Eq;
use str::str;
use std::map::Map;
use send_map::linear::LinearMap;
use std::json;
use std::json::ToJson;
//Toplevel public function for parsing. Args are taken from os::args()
pub fn docopt(doc: ~str) -> Result<LinearMap<~str, json::Json>, ~str> {
let argv = os::args();
docopt_ext(copy doc, copy argv)
}
/// Toplevel public function for parsing doc. Arguments passed explicitly
pub fn docopt_ext(doc: ~str, argv: ~[~str]) -> Result<LinearMap<~str, json::Json>, ~str> {
let mut options = LinearMap();
/* TODO: insert data to map here */
options.insert(~"Arguments", argv.to_json());
if doc == ~"trigger_error" {
Err(str::append(~"Error: ", doc))
}
else {
Ok(move options)
}
}
pub struct Option {
mut short: ~str,
mut long: ~str,
mut argcount: int,
mut value: ~str
}
/// Parse token and return option object
pub fn Option(short: ~str, long: ~str, argcount: int, value: ~str) -> Option {
Option {
short: move short,
long: move long,
argcount: move argcount,
value: move value
}
}
pub fn get_option() -> Option {
Option {
short: ~"",
long: ~"",
argcount: 0,
value: ~""
}
}
impl Option {
/// Parse token and return option object
fn parse(option_description: &str) {
let splitted = str::split_str_nonempty(
option_description.trim(), ~" ");
let mut (options, description) = match splitted.len() {
1 => (copy splitted[0], ~""),
2 => (copy splitted[0], copy splitted[1]),
_ => {io::println("Error: double space must appear only once");
fail}// Handle this situation more gracefully
};
let options = str::replace(options, ~",", ~" ");
let options = str::replace(options, ~"=", ~" ");
let splitted = str::split_char_nonempty(options, ' ');
for splitted.each() |part| {
if str::starts_with(*part, ~"--") {
self.long = copy *part;
}
else if str::starts_with(*part, ~"-") {
self.short = copy *part;
}
else {
self.argcount = 1;
}
}
if self.argcount > 0 {
let splitted_desc = description.split_str(~"[default: ");
self.value = match splitted_desc.len() {
1 => {~""},
2 => {splitted_desc[1].split_char(']')[0]},
_ => {io::println("Error: [default: VALUE] must \
appear only once");
fail} // May be handle this more gracefully
};
}
// TODO: parse default value '\[default: (.*)\]'
}
}
impl Option: Eq {
#[inline(always)]
pure fn eq(other: &Option) -> bool {
((self.short == other.short) && (self.long == other.long) && (self.argcount == other.argcount) && (self.value == other.value))
}
#[inline(always)]
pure fn ne(other: &Option) -> bool { !self.eq(other) }
}
/// Print usage
pub fn printable_usage(doc: ~str) -> ~str {
let splitted = str::split_str_nonempty(doc, ~"Usage:");
let (word_usage, usage) =match splitted.len() {
1 => (copy splitted[0], ~""),
2 => (copy splitted[0], copy splitted[1]),
_ => {io::println("Error in description: ``Usage:`` \
must appear only once");
fail // Handle more gracefully
}
};
fmt!("%s%s", word_usage, usage)
}
#[cfg(test)]
mod tests {
fn check_option(token: ~str, option_args: (~str, ~str, int, ~str)) {
let option = get_option();
option.parse(token);
let (short, long, argcount, value) = copy option_args;
assert option == Option(copy short, copy long,
copy argcount, copy value);
}
#[test]
fn test_option() {
check_option(~"-h", (~"-h", ~"", 0, ~""));
check_option(~"--help", (~"", ~"--help", 0, ~""));
check_option(~"-h --help", (~"-h", ~"--help", 0, ~""));
check_option(~"-h, --help", (~"-h", ~"--help", 0, ~""));
check_option(~"-h TOPIC", (~"-h", ~"", 1, ~""));
check_option(~"--help TOPIC", (~"", ~"--help", 1, ~""));
check_option(~"-h TOPIC --help TOPIC", (~"-h", ~"--help", 1, ~""));
check_option(~"-h TOPIC, --help TOPIC", (~"-h", ~"--help", 1, ~""));
check_option(~"-h TOPIC, --help=TOPIC", (~"-h", ~"--help", 1, ~""));
check_option(~"-h Description...", (~"-h", ~"", 0, ~""));
check_option(~"-h --help Description...", (~"-h", ~"--help", 0, ~""));
check_option(~"-h TOPIC Description...", (~"-h", ~"", 1, ~""));
check_option(~" -h", (~"-h", ~"", 0, ~""));
check_option(~"-h TOPIC Descripton... [default: 2]",
(~"-h", ~"", 1, ~"2"));
check_option(~"-h TOPIC Descripton... [default: topic-1]",
(~"-h", ~"", 1, ~"topic-1"));
check_option(~"--help=TOPIC ... [default: 3.14]",
(~"", ~"--help", 1, ~"3.14"));
check_option(~"-h, --help=DIR ... [default: ./]",
(~"-h", ~"--help", 1, ~"./"));
}
#[test]
fn test_docopt_ext_ok() {
let result = docopt_ext(~"Usage: my_program", ~[]);
assert result.is_ok();
}
// #[test]
// fn test_docopt_ext_err() {
// let result = docopt::docopt_ext(~"Usage: my_program", ~[~"-h"]);
// assert result.is_err();
// }
}