Skip to content

Commit 74c7457

Browse files
committed
Add GetOpts module to std
1 parent d0ed2e3 commit 74c7457

File tree

2 files changed

+250
-0
lines changed

2 files changed

+250
-0
lines changed

src/lib/GetOpts.rs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/* Simple getopt alternative. Construct a vector of options, either by using
2+
* reqopt, optopt, and optflag or by building them from components yourself,
3+
* and pass them to getopts, along with a vector of actual arguments (not
4+
* including argv[0]). You'll either get a failure code back, or a match.
5+
* You'll have to verify whether the amount of 'free' arguments in the match
6+
* is what you expect. Use opt_* accessors (bottom of the file) to get
7+
* argument values out of the match object.
8+
*/
9+
10+
import option.some;
11+
import option.none;
12+
13+
tag name { long(str); short(char); }
14+
tag hasarg { yes; no; maybe; }
15+
tag occur { req; optional; multi; }
16+
17+
type opt = rec(name name, hasarg hasarg, occur occur);
18+
19+
fn mkname(str nm) -> name {
20+
if (_str.char_len(nm) == 1u) { ret short(_str.char_at(nm, 0u)); }
21+
else { ret long(nm); }
22+
}
23+
fn reqopt(str name) -> opt {
24+
ret rec(name=mkname(name), hasarg=yes, occur=req);
25+
}
26+
fn optopt(str name) -> opt {
27+
ret rec(name=mkname(name), hasarg=yes, occur=optional);
28+
}
29+
fn optflag(str name) -> opt {
30+
ret rec(name=mkname(name), hasarg=no, occur=optional);
31+
}
32+
fn optmulti(str name) -> opt {
33+
ret rec(name=mkname(name), hasarg=yes, occur=multi);
34+
}
35+
36+
tag optval {
37+
val(str);
38+
given;
39+
}
40+
41+
type match = rec(vec[opt] opts, vec[mutable vec[optval]] vals, vec[str] free);
42+
43+
fn is_arg(str arg) -> bool {
44+
ret _str.byte_len(arg) > 1u && arg.(0) == '-' as u8;
45+
}
46+
fn name_str(name nm) -> str {
47+
alt (nm) {
48+
case (short(?ch)) {ret _str.from_char(ch);}
49+
case (long(?s)) {ret s;}
50+
}
51+
}
52+
53+
// FIXME rustboot workaround
54+
fn name_eq(name a, name b) -> bool {
55+
alt (a) {
56+
case (long(?a)) {
57+
alt (b) {
58+
case (long(?b)) { ret _str.eq(a, b); }
59+
case (_) { ret false; }
60+
}
61+
}
62+
case (_) { if (a == b) { ret true; } else {ret false; } }
63+
}
64+
}
65+
fn find_opt(vec[opt] opts, name nm) -> option.t[uint] {
66+
auto i = 0u;
67+
auto l = _vec.len[opt](opts);
68+
while (i < l) {
69+
if (name_eq(opts.(i).name, nm)) { ret some[uint](i); }
70+
i += 1u;
71+
}
72+
ret none[uint];
73+
}
74+
75+
tag fail_ {
76+
argument_missing(str);
77+
unrecognized_option(str);
78+
option_missing(str);
79+
option_duplicated(str);
80+
}
81+
82+
fn fail_str(fail_ f) -> str {
83+
alt (f) {
84+
case (argument_missing(?nm)) {
85+
ret "Argument to option '" + nm + "' missing.";
86+
}
87+
case (unrecognized_option(?nm)) {
88+
ret "Unrecognized option: '" + nm + "'.";
89+
}
90+
case (option_missing(?nm)) {
91+
ret "Required option '" + nm + "' missing.";
92+
}
93+
case (option_duplicated(?nm)) {
94+
ret "Option '" + nm + "' given more than once.";
95+
}
96+
}
97+
}
98+
99+
tag result {
100+
success(match);
101+
failure(fail_);
102+
}
103+
104+
fn getopts(vec[str] args, vec[opt] opts) -> result {
105+
auto n_opts = _vec.len[opt](opts);
106+
fn empty_(uint x) -> vec[optval]{ret _vec.empty[optval]();}
107+
auto f = empty_;
108+
auto vals = _vec.init_fn_mut[vec[optval]](f, n_opts);
109+
let vec[str] free = vec();
110+
111+
auto l = _vec.len[str](args);
112+
auto i = 0u;
113+
while (i < l) {
114+
auto cur = args.(i);
115+
auto curlen = _str.byte_len(cur);
116+
if (!is_arg(cur)) {
117+
_vec.push[str](free, cur);
118+
} else if (_str.eq(cur, "--")) {
119+
free += _vec.slice[str](args, i + 1u, l);
120+
break;
121+
} else {
122+
auto names;
123+
auto i_arg = option.none[str];
124+
if (cur.(1) == '-' as u8) {
125+
auto tail = _str.slice(cur, 2u, curlen);
126+
auto eq = _str.index(tail, '=' as u8);
127+
if (eq == -1) {
128+
names = vec(long(tail));
129+
} else {
130+
names = vec(long(_str.slice(tail, 0u, eq as uint)));
131+
i_arg = option.some[str]
132+
(_str.slice(tail, (eq as uint) + 1u, curlen - 2u));
133+
}
134+
} else {
135+
auto j = 1u;
136+
names = vec();
137+
while (j < curlen) {
138+
auto range = _str.char_range_at(cur, j);
139+
_vec.push[name](names, short(range._0));
140+
j = range._1;
141+
}
142+
}
143+
auto name_pos = 0u;
144+
for (name nm in names) {
145+
name_pos += 1u;
146+
auto optid;
147+
alt (find_opt(opts, nm)) {
148+
case (some[uint](?id)) {optid = id;}
149+
case (none[uint]) {
150+
ret failure(unrecognized_option(name_str(nm)));
151+
}
152+
}
153+
alt (opts.(optid).hasarg) {
154+
case (no) {
155+
_vec.push[optval](vals.(optid), given);
156+
}
157+
case (maybe) {
158+
if (!option.is_none[str](i_arg)) {
159+
_vec.push[optval](vals.(optid),
160+
val(option.get[str](i_arg)));
161+
} else if (name_pos < _vec.len[name](names) ||
162+
i + 1u == l || is_arg(args.(i + 1u))) {
163+
_vec.push[optval](vals.(optid), given);
164+
} else {
165+
i += 1u;
166+
_vec.push[optval](vals.(optid), val(args.(i)));
167+
}
168+
}
169+
case (yes) {
170+
if (!option.is_none[str](i_arg)) {
171+
_vec.push[optval](vals.(optid),
172+
val(option.get[str](i_arg)));
173+
} else if (i + 1u == l) {
174+
ret failure(argument_missing(name_str(nm)));
175+
} else {
176+
i += 1u;
177+
_vec.push[optval](vals.(optid), val(args.(i)));
178+
}
179+
}
180+
}
181+
}
182+
}
183+
i += 1u;
184+
}
185+
186+
i = 0u;
187+
while (i < n_opts) {
188+
auto n = _vec.len[optval](vals.(i));
189+
auto occ = opts.(i).occur;
190+
if (occ == req) {if (n == 0u) {
191+
ret failure(option_missing(name_str(opts.(i).name)));
192+
}}
193+
if (occ != multi) {if (n > 1u) {
194+
ret failure(option_duplicated(name_str(opts.(i).name)));
195+
}}
196+
i += 1u;
197+
}
198+
199+
ret success(rec(opts=opts, vals=vals, free=free));
200+
}
201+
202+
fn opt_vals(match m, str nm) -> vec[optval] {
203+
alt (find_opt(m.opts, mkname(nm))) {
204+
case (some[uint](?id)) { ret m.vals.(id); }
205+
case (none[uint]) {
206+
log_err "No option '" + nm + "' defined.";
207+
fail;
208+
}
209+
}
210+
}
211+
fn opt_val(match m, str nm) -> optval {
212+
ret opt_vals(m, nm).(0);
213+
}
214+
fn opt_present(match m, str nm) -> bool {
215+
ret _vec.len[optval](opt_vals(m, nm)) > 0u;
216+
}
217+
fn opt_str(match m, str nm) -> str {
218+
alt (opt_val(m, nm)) {
219+
case (val(?s)) { ret s; }
220+
case (_) { fail; }
221+
}
222+
}
223+
fn opt_strs(match m, str nm) -> vec[str] {
224+
let vec[str] acc = vec();
225+
for (optval v in opt_vals(m, nm)) {
226+
alt (v) {
227+
case (val(?s)) { _vec.push[str](acc, s); }
228+
case (_) {}
229+
}
230+
}
231+
ret acc;
232+
}
233+
fn opt_maybe_str(match m, str nm) -> option.t[str] {
234+
auto vals = opt_vals(m, nm);
235+
if (_vec.len[optval](vals) == 0u) { ret none[str]; }
236+
alt (vals.(0)) {
237+
case (val(?s)) { ret some[str](s); }
238+
case (_) { ret none[str]; }
239+
}
240+
}
241+
242+
// Local Variables:
243+
// mode: rust;
244+
// fill-column: 78;
245+
// indent-tabs-mode: nil
246+
// c-basic-offset: 4
247+
// buffer-file-coding-system: utf-8-unix
248+
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
249+
// End:

src/lib/std.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ mod ebml;
7070
mod UFind;
7171
mod ExtFmt;
7272
mod Box;
73+
mod GetOpts;
7374

7475
// Local Variables:
7576
// mode: rust;

0 commit comments

Comments
 (0)