1 use std::str::FromStr;
2
3 use assert_matches::assert_matches;
4
5 use gumdrop::Options;
6
7 const EMPTY: &'static [&'static str] = &[];
8
9 #[derive(Debug, Options)]
10 struct NoOpts { }
11
12 macro_rules! is_err {
13 ( $e:expr , |$ident:ident| $expr:expr ) => {
14 let $ident = $e.map(|_| ()).unwrap_err().to_string();
15 assert!($expr,
16 "error {:?} does not match `{}`", $ident, stringify!($expr));
17 };
18 ( $e:expr , $str:expr ) => {
19 assert_eq!($e.map(|_| ()).unwrap_err().to_string(), $str)
20 };
21 }
22
23 #[test]
test_hygiene()24 fn test_hygiene() {
25 // Define these aliases in local scope to ensure that generated code
26 // is using absolute paths, i.e. `::std::result::Result`
27 #[allow(dead_code)] struct AsRef;
28 #[allow(dead_code)] struct Default;
29 #[allow(dead_code)] struct FromStr;
30 #[allow(dead_code)] struct Option;
31 #[allow(dead_code)] struct Some;
32 #[allow(dead_code)] struct None;
33 #[allow(dead_code)] struct Options;
34 #[allow(dead_code)] struct Result;
35 #[allow(dead_code)] struct Ok;
36 #[allow(dead_code)] struct Err;
37 #[allow(dead_code)] struct String;
38 #[allow(dead_code)] struct ToString;
39 #[allow(dead_code)] struct Vec;
40
41 #[derive(Options)]
42 struct Opts {
43 a: i32,
44 b: ::std::string::String,
45 c: ::std::option::Option<::std::string::String>,
46 d: ::std::option::Option<i32>,
47 e: ::std::vec::Vec<i32>,
48 f: ::std::vec::Vec<::std::string::String>,
49 g: ::std::option::Option<(i32, i32)>,
50
51 #[options(command)]
52 cmd: ::std::option::Option<Cmd>,
53 }
54
55 #[derive(Options)]
56 enum Cmd {
57 Foo(FooOpts),
58 Bar(BarOpts),
59 }
60
61 #[derive(Options)]
62 struct FooOpts {
63 #[options(free)]
64 free: ::std::vec::Vec<::std::string::String>,
65 a: i32,
66 }
67
68 #[derive(Options)]
69 struct BarOpts {
70 #[options(free)]
71 first: ::std::option::Option<::std::string::String>,
72 #[options(free)]
73 rest: ::std::vec::Vec<::std::string::String>,
74 a: i32,
75 }
76
77 // This is basically just a compile-pass test, so whatever.
78 }
79
80 #[test]
test_command()81 fn test_command() {
82 #[derive(Options)]
83 struct Opts {
84 help: bool,
85
86 #[options(command)]
87 command: Option<Command>,
88 }
89
90 #[derive(Debug, Options)]
91 enum Command {
92 Foo(FooOpts),
93 Bar(BarOpts),
94 #[options(name = "bzzz")]
95 Baz(NoOpts),
96 FooBar(NoOpts),
97 FooXYZ(NoOpts),
98 }
99
100 #[derive(Debug, Options)]
101 struct FooOpts {
102 foo: Option<String>,
103 }
104
105 #[derive(Debug, Options)]
106 struct BarOpts {
107 #[options(free)]
108 free: Vec<String>,
109 }
110
111 let opts = Opts::parse_args_default(EMPTY).unwrap();
112 assert_eq!(opts.command.is_none(), true);
113
114 let opts = Opts::parse_args_default(&["-h"]).unwrap();
115 assert_eq!(opts.help, true);
116 assert_eq!(opts.command.is_none(), true);
117
118 let opts = Opts::parse_args_default(&["-h", "foo", "--foo", "x"]).unwrap();
119 assert_eq!(opts.help, true);
120 let cmd = opts.command.unwrap();
121 assert_matches!(cmd, Command::Foo(FooOpts{foo: Some(ref foo)}) if foo == "x");
122
123 let opts = Opts::parse_args_default(&["--", "foo"]).unwrap();
124 assert_eq!(opts.help, false);
125 let cmd = opts.command.unwrap();
126 assert_matches!(cmd, Command::Foo(_));
127
128 let opts = Opts::parse_args_default(&["bar", "free"]).unwrap();
129 let cmd = opts.command.unwrap();
130 assert_matches!(cmd, Command::Bar(ref bar) if bar.free == ["free"]);
131
132 let opts = Opts::parse_args_default(&["bzzz"]).unwrap();
133 let cmd = opts.command.unwrap();
134 assert_matches!(cmd, Command::Baz(_));
135
136 let opts = Opts::parse_args_default(&["foo-bar"]).unwrap();
137 let cmd = opts.command.unwrap();
138 assert_matches!(cmd, Command::FooBar(_));
139
140 let opts = Opts::parse_args_default(&["foo-x-y-z"]).unwrap();
141 let cmd = opts.command.unwrap();
142 assert_matches!(cmd, Command::FooXYZ(_));
143
144 is_err!(Opts::parse_args_default(&["foo", "-h"]),
145 "unrecognized option `-h`");
146 is_err!(Opts::parse_args_default(&["baz"]),
147 "unrecognized command `baz`");
148 }
149
150 #[test]
test_command_name()151 fn test_command_name() {
152 #[derive(Options)]
153 struct Opts {
154 help: bool,
155
156 #[options(command)]
157 command: Option<Command>,
158 }
159
160 #[derive(Debug, Options)]
161 enum Command {
162 Foo(NoOpts),
163 Bar(NoOpts),
164 #[options(name = "bzzz")]
165 Baz(NoOpts),
166 BoopyDoop(NoOpts),
167 }
168
169
170 let opts = Opts::parse_args_default(&["foo"]).unwrap();
171 assert_matches!(opts.command_name(), Some("foo"));
172
173 let opts = Opts::parse_args_default(&["bar"]).unwrap();
174 assert_matches!(opts.command_name(), Some("bar"));
175
176 let opts = Opts::parse_args_default(&["bzzz"]).unwrap();
177 assert_matches!(opts.command_name(), Some("bzzz"));
178
179 let opts = Opts::parse_args_default(&["boopy-doop"]).unwrap();
180 assert_matches!(opts.command_name(), Some("boopy-doop"));
181 }
182
183 #[test]
test_command_usage()184 fn test_command_usage() {
185 #[derive(Options)]
186 struct Opts {
187 #[options(help = "help me!")]
188 help: bool,
189
190 #[options(command)]
191 command: Option<Command>,
192 }
193
194 #[derive(Options)]
195 enum Command {
196 #[options(help = "foo help")]
197 Foo(NoOpts),
198 #[options(help = "bar help")]
199 Bar(NoOpts),
200 #[options(help = "baz help")]
201 #[options(name = "bzzz")]
202 Baz(NoOpts),
203 }
204
205 assert_eq!(Command::usage(), &"
206 foo foo help
207 bar bar help
208 bzzz baz help"
209 // Skip leading newline
210 [1..]);
211
212 assert_eq!(Command::command_list(), Some(Command::usage()));
213 assert_eq!(Opts::command_list(), Some(Command::usage()));
214 }
215
216 #[test]
test_opt_bool()217 fn test_opt_bool() {
218 #[derive(Options)]
219 struct Opts {
220 switch: bool,
221 }
222
223 let opts = Opts::parse_args_default(&["--switch"]).unwrap();
224 assert_eq!(opts.switch, true);
225
226 let opts = Opts::parse_args_default(&["-s"]).unwrap();
227 assert_eq!(opts.switch, true);
228
229 is_err!(Opts::parse_args_default(&["--switch=x"]),
230 "option `--switch` does not accept an argument");
231 }
232
233 #[test]
test_opt_string()234 fn test_opt_string() {
235 #[derive(Options)]
236 struct Opts {
237 foo: String,
238 }
239
240 let opts = Opts::parse_args_default(&["--foo", "value"]).unwrap();
241 assert_eq!(opts.foo, "value");
242
243 let opts = Opts::parse_args_default(&["-f", "value"]).unwrap();
244 assert_eq!(opts.foo, "value");
245
246 let opts = Opts::parse_args_default(&["-fvalue"]).unwrap();
247 assert_eq!(opts.foo, "value");
248 }
249
250 #[test]
test_opt_int()251 fn test_opt_int() {
252 #[derive(Options)]
253 struct Opts {
254 number: i32,
255 }
256
257 let opts = Opts::parse_args_default(&["--number", "123"]).unwrap();
258 assert_eq!(opts.number, 123);
259
260 let opts = Opts::parse_args_default(&["-n", "123"]).unwrap();
261 assert_eq!(opts.number, 123);
262
263 let opts = Opts::parse_args_default(&["-n123"]).unwrap();
264 assert_eq!(opts.number, 123);
265
266 is_err!(Opts::parse_args_default(&["-nfail"]),
267 |e| e.starts_with("invalid argument to option `-n`: "));
268 is_err!(Opts::parse_args_default(&["--number", "fail"]),
269 |e| e.starts_with("invalid argument to option `--number`: "));
270 is_err!(Opts::parse_args_default(&["--number=fail"]),
271 |e| e.starts_with("invalid argument to option `--number`: "));
272 }
273
274 #[test]
test_opt_tuple()275 fn test_opt_tuple() {
276 #[derive(Options)]
277 struct Opts {
278 alpha: (i32, i32),
279 bravo: Option<(i32, i32, i32)>,
280 charlie: Vec<(i32, i32, i32, i32)>,
281 #[options(free)]
282 free: Vec<String>,
283 }
284
285 let opts = Opts::parse_args_default(&[
286 "--alpha", "1", "2",
287 "--bravo", "11", "12", "13",
288 "--charlie", "21", "22", "23", "24",
289 "--charlie", "31", "32", "33", "34",
290 "free",
291 ]).unwrap();
292
293 assert_eq!(opts.alpha, (1, 2));
294 assert_eq!(opts.bravo, Some((11, 12, 13)));
295 assert_eq!(opts.charlie, vec![
296 (21, 22, 23, 24),
297 (31, 32, 33, 34),
298 ]);
299 assert_eq!(opts.free, vec!["free".to_owned()]);
300 }
301
302 #[test]
test_opt_tuple_error()303 fn test_opt_tuple_error() {
304 #[derive(Options)]
305 struct Opts {
306 foo: Option<(i32, i32)>,
307 }
308
309 is_err!(Opts::parse_args_default(&["--foo"]),
310 "insufficient arguments to option `--foo`: expected 2; found 0");
311 is_err!(Opts::parse_args_default(&["--foo=0", "1"]),
312 "option `--foo` expects 2 arguments; found 1");
313 is_err!(Opts::parse_args_default(&["--foo", "0"]),
314 "insufficient arguments to option `--foo`: expected 2; found 1");
315 }
316
317 #[test]
test_opt_push()318 fn test_opt_push() {
319 #[derive(Options)]
320 struct Opts {
321 thing: Vec<String>,
322 }
323
324 let opts = Opts::parse_args_default(EMPTY).unwrap();
325 assert!(opts.thing.is_empty());
326
327 let opts = Opts::parse_args_default(
328 &["-t", "a", "-tb", "--thing=c", "--thing", "d"]).unwrap();
329 assert_eq!(opts.thing, ["a", "b", "c", "d"]);
330 }
331
332 #[test]
test_opt_count()333 fn test_opt_count() {
334 #[derive(Options)]
335 struct Opts {
336 #[options(count)]
337 number: i32,
338 }
339
340 let opts = Opts::parse_args_default(EMPTY).unwrap();
341 assert_eq!(opts.number, 0);
342
343 let opts = Opts::parse_args_default(&["--number"]).unwrap();
344 assert_eq!(opts.number, 1);
345
346 let opts = Opts::parse_args_default(&["-nnn"]).unwrap();
347 assert_eq!(opts.number, 3);
348 }
349
350 #[test]
test_opt_long()351 fn test_opt_long() {
352 #[derive(Options)]
353 struct Opts {
354 #[options(long = "thing", no_short)]
355 foo: bool,
356 }
357
358 let opts = Opts::parse_args_default(&["--thing"]).unwrap();
359 assert_eq!(opts.foo, true);
360
361 is_err!(Opts::parse_args_default(&["-f"]),
362 "unrecognized option `-f`");
363 is_err!(Opts::parse_args_default(&["--foo"]),
364 "unrecognized option `--foo`");
365 }
366
367 #[test]
test_opt_short()368 fn test_opt_short() {
369 #[derive(Options)]
370 struct Opts {
371 #[options(short = "x", no_long)]
372 foo: bool,
373 }
374
375 let opts = Opts::parse_args_default(&["-x"]).unwrap();
376 assert_eq!(opts.foo, true);
377
378 is_err!(Opts::parse_args_default(&["-f"]),
379 "unrecognized option `-f`");
380 is_err!(Opts::parse_args_default(&["--foo"]),
381 "unrecognized option `--foo`");
382 }
383
384 #[test]
test_opt_short_override()385 fn test_opt_short_override() {
386 // Ensures that the generated code sees the manual assignment of short
387 // option for `option_1` before generating a short option for `option_0`.
388 // Thus, giving `option_0` an automatic short option of `O`,
389 // rather than causing a collision.
390 #[derive(Options)]
391 struct Opts {
392 #[options(no_long)]
393 option_0: bool,
394 #[options(short = "o", no_long)]
395 option_1: bool,
396 }
397
398 let opts = Opts::parse_args_default(&["-o"]).unwrap();
399 assert_eq!(opts.option_0, false);
400 assert_eq!(opts.option_1, true);
401
402 let opts = Opts::parse_args_default(&["-O"]).unwrap();
403 assert_eq!(opts.option_0, true);
404 assert_eq!(opts.option_1, false);
405 }
406
407 #[test]
test_opt_free()408 fn test_opt_free() {
409 #[derive(Options)]
410 struct Opts {
411 #[options(free)]
412 free: Vec<String>,
413 }
414
415 let opts = Opts::parse_args_default(&["a", "b", "c"]).unwrap();
416 assert_eq!(opts.free, ["a", "b", "c"]);
417 }
418
419 #[test]
test_opt_no_free()420 fn test_opt_no_free() {
421 #[derive(Options)]
422 struct Opts {
423 }
424
425 assert!(Opts::parse_args_default(EMPTY).is_ok());
426 is_err!(Opts::parse_args_default(&["a"]),
427 "unexpected free argument `a`");
428 }
429
430 #[test]
test_typed_free()431 fn test_typed_free() {
432 #[derive(Options)]
433 struct Opts {
434 #[options(free)]
435 free: Vec<i32>,
436 }
437
438 let opts = Opts::parse_args_default(&["1", "2", "3"]).unwrap();
439 assert_eq!(opts.free, [1, 2, 3]);
440 }
441
442 #[test]
test_multi_free()443 fn test_multi_free() {
444 #[derive(Options)]
445 struct Opts {
446 #[options(free, help = "alpha help")]
447 alpha: u32,
448 #[options(free, help = "bravo help")]
449 bravo: Option<String>,
450 #[options(free, help = "charlie help")]
451 charlie: Option<u32>,
452 }
453
454 let opts = Opts::parse_args_default(EMPTY).unwrap();
455
456 assert_eq!(opts.alpha, 0);
457 assert_eq!(opts.bravo, None);
458 assert_eq!(opts.charlie, None);
459
460 let opts = Opts::parse_args_default(&["1"]).unwrap();
461
462 assert_eq!(opts.alpha, 1);
463 assert_eq!(opts.bravo, None);
464 assert_eq!(opts.charlie, None);
465
466 let opts = Opts::parse_args_default(&["1", "two", "3"]).unwrap();
467
468 assert_eq!(opts.alpha, 1);
469 assert_eq!(opts.bravo, Some("two".to_owned()));
470 assert_eq!(opts.charlie, Some(3));
471
472 is_err!(Opts::parse_args_default(&["1", "two", "3", "4"]),
473 "unexpected free argument `4`");
474
475 assert_eq!(Opts::usage(), &"
476 Positional arguments:
477 alpha alpha help
478 bravo bravo help
479 charlie charlie help"
480 // Skip leading newline
481 [1..]);
482
483 #[derive(Options)]
484 struct ManyOpts {
485 #[options(free, help = "alpha help")]
486 alpha: u32,
487 #[options(free, help = "bravo help")]
488 bravo: Option<String>,
489 #[options(free, help = "charlie help")]
490 charlie: Option<u32>,
491 #[options(free)]
492 rest: Vec<String>,
493 }
494
495 let opts = ManyOpts::parse_args_default(EMPTY).unwrap();
496
497 assert_eq!(opts.alpha, 0);
498 assert_eq!(opts.bravo, None);
499 assert_eq!(opts.charlie, None);
500 assert_eq!(opts.rest, Vec::<String>::new());
501
502 let opts = ManyOpts::parse_args_default(&["1", "two", "3", "4", "five", "VI"]).unwrap();
503
504 assert_eq!(opts.alpha, 1);
505 assert_eq!(opts.bravo, Some("two".to_owned()));
506 assert_eq!(opts.charlie, Some(3));
507 assert_eq!(opts.rest, vec!["4".to_owned(), "five".to_owned(), "VI".to_owned()]);
508 }
509
510 #[test]
test_usage()511 fn test_usage() {
512 #[derive(Options)]
513 struct Opts {
514 #[options(help = "alpha help")]
515 alpha: bool,
516 #[options(no_short, help = "bravo help")]
517 bravo: String,
518 #[options(no_long, help = "charlie help")]
519 charlie: bool,
520 #[options(help = "delta help", meta = "X")]
521 delta: i32,
522 #[options(help = "echo help", meta = "Y")]
523 echo: Vec<String>,
524 #[options(help = "foxtrot help", meta = "Z", default = "99")]
525 foxtrot: u32,
526 #[options(no_short, help = "long option help")]
527 very_very_long_option_with_very_very_long_name: bool,
528 }
529
530 assert_eq!(Opts::usage(), &"
531 Optional arguments:
532 -a, --alpha alpha help
533 --bravo BRAVO bravo help
534 -c charlie help
535 -d, --delta X delta help
536 -e, --echo Y echo help
537 -f, --foxtrot Z foxtrot help (default: 99)
538 --very-very-long-option-with-very-very-long-name
539 long option help"
540 // Skip leading newline
541 [1..]);
542
543 #[derive(Options)]
544 struct TupleOpts {
545 #[options(help = "alpha help")]
546 alpha: (),
547 #[options(help = "bravo help")]
548 bravo: (i32,),
549 #[options(help = "charlie help")]
550 charlie: (i32, i32),
551 #[options(help = "delta help")]
552 delta: (i32, i32, i32),
553 #[options(help = "echo help")]
554 echo: (i32, i32, i32, i32),
555 }
556
557 assert_eq!(TupleOpts::usage(), &"
558 Optional arguments:
559 -a, --alpha alpha help
560 -b, --bravo BRAVO bravo help
561 -c, --charlie CHARLIE VALUE
562 charlie help
563 -d, --delta DELTA VALUE0 VALUE1
564 delta help
565 -e, --echo ECHO VALUE0 VALUE1 VALUE2
566 echo help"
567 // Skip leading newline
568 [1..]);
569
570 #[derive(Options)]
571 struct FreeOpts {
572 #[options(free, help = "a help")]
573 a: u32,
574 #[options(free, help = "b help")]
575 b: u32,
576 #[options(free, help = "c help")]
577 c: u32,
578
579 #[options(help = "option help")]
580 option: bool,
581 }
582
583 assert_eq!(FreeOpts::usage(), &"
584 Positional arguments:
585 a a help
586 b b help
587 c c help
588
589 Optional arguments:
590 -o, --option option help"
591 // Skip leading newline
592 [1..]);
593 }
594
595 #[test]
test_help_flag()596 fn test_help_flag() {
597 #[derive(Options)]
598 struct Opts {
599 help: bool,
600 }
601
602 let opts = Opts::parse_args_default(EMPTY).unwrap();
603 assert_eq!(opts.help_requested(), false);
604
605 let opts = Opts::parse_args_default(&["--help"]).unwrap();
606 assert_eq!(opts.help_requested(), true);
607 }
608
609 #[test]
test_no_help_flag()610 fn test_no_help_flag() {
611 #[derive(Options)]
612 struct Opts {
613 #[options(no_help_flag)]
614 help: bool,
615 }
616
617 let opts = Opts::parse_args_default(&["--help"]).unwrap();
618 assert_eq!(opts.help_requested(), false);
619 }
620
621 #[test]
test_many_help_flags()622 fn test_many_help_flags() {
623 #[derive(Options)]
624 struct Opts {
625 #[options(help_flag)]
626 help: bool,
627 #[options(help_flag)]
628 halp: bool,
629 #[options(help_flag)]
630 help_please: bool,
631 }
632
633 let opts = Opts::parse_args_default(EMPTY).unwrap();
634 assert_eq!(opts.help_requested(), false);
635
636 let opts = Opts::parse_args_default(&["--help"]).unwrap();
637 assert_eq!(opts.help_requested(), true);
638
639 let opts = Opts::parse_args_default(&["--halp"]).unwrap();
640 assert_eq!(opts.help_requested(), true);
641
642 let opts = Opts::parse_args_default(&["--help-please"]).unwrap();
643 assert_eq!(opts.help_requested(), true);
644 }
645
646 #[test]
test_help_flag_command()647 fn test_help_flag_command() {
648 #[derive(Options)]
649 struct Opts {
650 help: bool,
651
652 #[options(command)]
653 cmd: Option<Cmd>,
654 }
655
656 #[derive(Options)]
657 struct Opts2 {
658 #[options(command)]
659 cmd: Option<Cmd>,
660 }
661
662 #[derive(Options)]
663 struct Opts3 {
664 help: bool,
665 #[options(help_flag)]
666 help2: bool,
667
668 #[options(command)]
669 cmd: Option<Cmd>,
670 }
671
672 #[derive(Options)]
673 enum Cmd {
674 Foo(CmdOpts),
675 Bar(CmdOpts),
676 Baz(CmdOpts),
677 }
678
679 #[derive(Options)]
680 struct CmdOpts {
681 help: bool,
682 }
683
684 let opts = Opts::parse_args_default(EMPTY).unwrap();
685 assert_eq!(opts.help_requested(), false);
686
687 let opts = Opts::parse_args_default(&["-h"]).unwrap();
688 assert_eq!(opts.help_requested(), true);
689
690 let opts = Opts::parse_args_default(&["foo", "-h"]).unwrap();
691 assert_eq!(opts.help_requested(), true);
692
693 let opts = Opts::parse_args_default(&["bar", "-h"]).unwrap();
694 assert_eq!(opts.help_requested(), true);
695
696 let opts = Opts::parse_args_default(&["baz", "-h"]).unwrap();
697 assert_eq!(opts.help_requested(), true);
698
699 let opts = Opts2::parse_args_default(EMPTY).unwrap();
700 assert_eq!(opts.help_requested(), false);
701
702 let opts = Opts3::parse_args_default(EMPTY).unwrap();
703 assert_eq!(opts.help_requested(), false);
704 }
705
706 #[test]
test_type_attrs()707 fn test_type_attrs() {
708 #[derive(Options)]
709 #[options(no_help_flag, no_short, no_long)]
710 struct Opts {
711 #[options(long = "help")]
712 help: bool,
713 #[options(long = "foo")]
714 foo: bool,
715 #[options(short = "b")]
716 bar: bool,
717 }
718
719 is_err!(Opts::parse_args_default(&["-f"]),
720 "unrecognized option `-f`");
721 is_err!(Opts::parse_args_default(&["--bar"]),
722 "unrecognized option `--bar`");
723 is_err!(Opts::parse_args_default(&["-h"]),
724 "unrecognized option `-h`");
725
726 let opts = Opts::parse_args_default(&["--help"]).unwrap();
727 assert_eq!(opts.help, true);
728 assert_eq!(opts.help_requested(), false);
729
730 let opts = Opts::parse_args_default(&["--foo"]).unwrap();
731 assert_eq!(opts.foo, true);
732
733 let opts = Opts::parse_args_default(&["-b"]).unwrap();
734 assert_eq!(opts.bar, true);
735
736 #[derive(Options)]
737 #[options(no_short)]
738 struct Opts2 {
739 foo: bool,
740 #[options(short = "b")]
741 bar: bool,
742 }
743
744 is_err!(Opts2::parse_args_default(&["-f"]),
745 "unrecognized option `-f`");
746
747 let opts = Opts2::parse_args_default(&["--foo", "-b"]).unwrap();
748 assert_eq!(opts.foo, true);
749 assert_eq!(opts.bar, true);
750
751 let opts = Opts2::parse_args_default(&["--bar"]).unwrap();
752 assert_eq!(opts.bar, true);
753
754 #[derive(Options)]
755 #[options(no_long)]
756 struct Opts3 {
757 foo: bool,
758 #[options(long = "bar")]
759 bar: bool,
760 }
761
762 is_err!(Opts3::parse_args_default(&["--foo"]),
763 "unrecognized option `--foo`");
764
765 let opts = Opts3::parse_args_default(&["--bar"]).unwrap();
766 assert_eq!(opts.bar, true);
767
768 let opts = Opts3::parse_args_default(&["-f", "-b"]).unwrap();
769 assert_eq!(opts.foo, true);
770 assert_eq!(opts.bar, true);
771
772 #[derive(Options)]
773 #[options(no_help_flag)]
774 struct Opts4 {
775 #[options(help_flag)]
776 help: bool,
777 }
778
779 let opts = Opts4::parse_args_default(&["-h"]).unwrap();
780 assert_eq!(opts.help, true);
781 assert_eq!(opts.help_requested(), true);
782
783 #[derive(Options)]
784 #[options(required)]
785 struct Opts5 {
786 #[options(no_long)]
787 foo: i32,
788 #[options(not_required)]
789 bar: i32,
790 }
791
792 is_err!(Opts5::parse_args_default(EMPTY),
793 "missing required option `-f`");
794
795 let opts = Opts5::parse_args_default(&["-f", "1"]).unwrap();
796 assert_eq!(opts.foo, 1);
797 assert_eq!(opts.bar, 0);
798
799 let opts = Opts5::parse_args_default(&["-f", "1", "--bar", "2"]).unwrap();
800 assert_eq!(opts.foo, 1);
801 assert_eq!(opts.bar, 2);
802 }
803
804 #[test]
test_required()805 fn test_required() {
806 #[derive(Options)]
807 struct Opts {
808 #[options(required)]
809 foo: i32,
810 optional: i32,
811 }
812
813 #[derive(Options)]
814 struct Opts2 {
815 #[options(command, required)]
816 command: Option<Cmd>,
817 optional: i32,
818 }
819
820 #[derive(Options)]
821 enum Cmd {
822 Foo(NoOpts),
823 }
824
825 #[derive(Options)]
826 struct Opts3 {
827 #[options(free, required)]
828 bar: i32,
829 optional: i32,
830 }
831
832 is_err!(Opts::parse_args_default(EMPTY),
833 "missing required option `--foo`");
834 is_err!(Opts2::parse_args_default(EMPTY),
835 "missing required command");
836 is_err!(Opts3::parse_args_default(EMPTY),
837 "missing required free argument");
838
839 let opts = Opts::parse_args_default(&["-f", "1"]).unwrap();
840 assert_eq!(opts.foo, 1);
841 let opts = Opts::parse_args_default(&["-f1"]).unwrap();
842 assert_eq!(opts.foo, 1);
843 let opts = Opts::parse_args_default(&["--foo", "1"]).unwrap();
844 assert_eq!(opts.foo, 1);
845 let opts = Opts::parse_args_default(&["--foo=1"]).unwrap();
846 assert_eq!(opts.foo, 1);
847
848 let opts = Opts2::parse_args_default(&["foo"]).unwrap();
849 assert!(opts.command.is_some());
850
851 let opts = Opts3::parse_args_default(&["1"]).unwrap();
852 assert_eq!(opts.bar, 1);
853 }
854
855 #[test]
test_required_help()856 fn test_required_help() {
857 #[derive(Options)]
858 struct Opts {
859 #[options(required)]
860 thing: Option<String>,
861 help: bool,
862 }
863
864 #[derive(Options)]
865 struct Opts2 {
866 #[options(required)]
867 thing: Option<String>,
868 help: bool,
869 #[options(help_flag)]
870 secondary_help: bool,
871 }
872
873 let opts = Opts::parse_args_default(&["-h"]).unwrap();
874 assert_eq!(opts.help, true);
875
876 let opts = Opts2::parse_args_default(&["--secondary-help"]).unwrap();
877 assert_eq!(opts.secondary_help, true);
878 }
879
880 #[test]
test_parse()881 fn test_parse() {
882 #[derive(Options)]
883 struct Opts {
884 #[options(help = "foo", parse(from_str = "parse_foo"))]
885 foo: Option<Foo>,
886 #[options(help = "bar", parse(try_from_str = "parse_bar"))]
887 bar: Option<Bar>,
888 #[options(help = "baz", parse(from_str))]
889 baz: Option<Baz>,
890 #[options(help = "quux", parse(try_from_str))]
891 quux: Option<Quux>,
892 }
893
894 #[derive(Debug)]
895 struct Foo(String);
896 #[derive(Debug)]
897 struct Bar(u32);
898 #[derive(Debug)]
899 struct Baz(String);
900 #[derive(Debug)]
901 struct Quux(u32);
902
903 fn parse_foo(s: &str) -> Foo { Foo(s.to_owned()) }
904 fn parse_bar(s: &str) -> Result<Bar, <u32 as FromStr>::Err> { s.parse().map(Bar) }
905
906 impl<'a> From<&'a str> for Baz {
907 fn from(s: &str) -> Baz {
908 Baz(s.to_owned())
909 }
910 }
911
912 impl FromStr for Quux {
913 type Err = <u32 as FromStr>::Err;
914
915 fn from_str(s: &str) -> Result<Quux, Self::Err> {
916 s.parse().map(Quux)
917 }
918 }
919
920 let opts = Opts::parse_args_default(&[
921 "-ffoo", "--bar=123", "--baz", "sup", "-q", "456"]).unwrap();
922 assert_matches!(opts.foo, Some(Foo(ref s)) if s == "foo");
923 assert_matches!(opts.bar, Some(Bar(123)));
924 assert_matches!(opts.baz, Some(Baz(ref s)) if s == "sup");
925 assert_matches!(opts.quux, Some(Quux(456)));
926
927 is_err!(Opts::parse_args_default(&["--bar", "xyz"]),
928 |e| e.starts_with("invalid argument to option `--bar`: "));
929 is_err!(Opts::parse_args_default(&["--quux", "xyz"]),
930 |e| e.starts_with("invalid argument to option `--quux`: "));
931 }
932
933 #[test]
test_default()934 fn test_default() {
935 #[derive(Options)]
936 struct Opts {
937 foo: u32,
938 #[options(default = "123")]
939 bar: u32,
940 #[options(default = "456")]
941 baz: Baz,
942 #[options(count, default = "789")]
943 count: u32,
944 }
945
946 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
947 struct Baz(u32);
948
949 impl FromStr for Baz {
950 type Err = <u32 as FromStr>::Err;
951
952 fn from_str(s: &str) -> Result<Self, Self::Err> {
953 s.parse().map(Baz)
954 }
955 }
956
957 let opts = Opts::parse_args_default(EMPTY).unwrap();
958 assert_eq!(opts.foo, 0);
959 assert_eq!(opts.bar, 123);
960 assert_eq!(opts.baz, Baz(456));
961 assert_eq!(opts.count, 789);
962
963 let opts = Opts::parse_args_default(&["-b99", "--baz=4387", "-c", "-f1"]).unwrap();
964 assert_eq!(opts.foo, 1);
965 assert_eq!(opts.bar, 99);
966 assert_eq!(opts.baz, Baz(4387));
967 assert_eq!(opts.count, 790);
968 }
969
970 #[test]
test_failed_default()971 fn test_failed_default() {
972 #[derive(Options)]
973 struct Opts {
974 #[options(default = "lolwut")]
975 foo: u32,
976 }
977
978 is_err!(Opts::parse_args_default(EMPTY),
979 |e| e.starts_with(r#"invalid default value for `foo` ("lolwut"): "#));
980 }
981
982 #[test]
test_default_parse()983 fn test_default_parse() {
984 #[derive(Options)]
985 struct Opts {
986 #[options(default = "1", parse(try_from_str = "parse_foo"))]
987 foo: Foo,
988 }
989
990 #[derive(Debug, Eq, PartialEq)]
991 struct Foo(u32);
992
993 fn parse_foo(s: &str) -> Result<Foo, <u32 as FromStr>::Err> { s.parse().map(Foo) }
994
995 let opts = Opts::parse_args_default(EMPTY).unwrap();
996 assert_eq!(opts.foo, Foo(1));
997 }
998
999 #[test]
test_multi()1000 fn test_multi() {
1001 use std::collections::VecDeque;
1002
1003 #[derive(Options)]
1004 struct Opts {
1005 #[options(multi = "push_back")]
1006 foo: VecDeque<String>,
1007 }
1008
1009 #[derive(Options)]
1010 struct Opts2 {
1011 #[options(multi = "push_back")]
1012 foo: VecDeque<(i32, i32)>,
1013 }
1014
1015 #[derive(Options)]
1016 struct Opts3 {
1017 #[options(free, multi = "push_front")]
1018 free: VecDeque<i32>,
1019 }
1020
1021 let opts = Opts::parse_args_default(&["-f", "foo", "-f", "bar"]).unwrap();
1022 assert_eq!(opts.foo, ["foo", "bar"]);
1023
1024 let opts = Opts2::parse_args_default(&["-f", "1", "2", "-f", "3", "4"]).unwrap();
1025 assert_eq!(opts.foo, [(1, 2), (3, 4)]);
1026
1027 let opts = Opts3::parse_args_default(&["1", "2", "3"]).unwrap();
1028 assert_eq!(opts.free, [3, 2, 1]);
1029 }
1030
1031 #[test]
test_no_multi()1032 fn test_no_multi() {
1033 #[derive(Options)]
1034 struct Opts {
1035 #[options(no_multi, parse(from_str = "comma_list"))]
1036 list_things: Vec<String>,
1037 }
1038
1039 #[derive(Options)]
1040 #[options(no_multi)]
1041 struct Opts2 {
1042 #[options(parse(from_str = "comma_list"))]
1043 list_things: Vec<String>,
1044 }
1045
1046 #[derive(Options)]
1047 struct Opts3 {
1048 #[options(free, no_multi, parse(from_str = "comma_list"))]
1049 list_things: Vec<String>,
1050 }
1051
1052 fn comma_list(s: &str) -> Vec<String> {
1053 s.split(',').map(|s| s.to_string()).collect()
1054 }
1055
1056 let opts = Opts::parse_args_default(&["-l", "foo,bar,baz"]).unwrap();
1057 assert_eq!(opts.list_things, ["foo", "bar", "baz"]);
1058
1059 let opts = Opts2::parse_args_default(&["-l", "foo,bar,baz"]).unwrap();
1060 assert_eq!(opts.list_things, ["foo", "bar", "baz"]);
1061
1062 let opts = Opts3::parse_args_default(&["foo,bar,baz"]).unwrap();
1063 assert_eq!(opts.list_things, ["foo", "bar", "baz"]);
1064
1065 is_err!(Opts3::parse_args_default(&["foo,bar,baz", "error"]),
1066 "unexpected free argument `error`");
1067 }
1068
1069 #[test]
test_doc_help()1070 fn test_doc_help() {
1071 /// type-level help comment
1072 #[derive(Options)]
1073 struct Opts {
1074 /// free help comment
1075 #[options(free)]
1076 free: i32,
1077 /// help comment
1078 foo: i32,
1079 /// help comment
1080 #[options(help = "help attribute")]
1081 bar: i32,
1082 }
1083
1084 #[derive(Options)]
1085 enum Cmd {
1086 /// help comment
1087 Alpha(NoOpts),
1088 /// help comment
1089 #[options(help = "help attribute")]
1090 Bravo(NoOpts),
1091 }
1092
1093 assert_eq!(Opts::usage(), &"
1094 type-level help comment
1095
1096 Positional arguments:
1097 free free help comment
1098
1099 Optional arguments:
1100 -f, --foo FOO help comment
1101 -b, --bar BAR help attribute"
1102 // Skip leading newline
1103 [1..]);
1104
1105 assert_eq!(Cmd::usage(), &"
1106 alpha help comment
1107 bravo help attribute"
1108 // Skip leading newline
1109 [1..]);
1110 }
1111
1112 #[test]
test_failed_parse_free()1113 fn test_failed_parse_free() {
1114 #[derive(Options)]
1115 struct Opts {
1116 #[options(free)]
1117 foo: u32,
1118 #[options(free, parse(try_from_str = "parse"))]
1119 bar: u32,
1120 #[options(free)]
1121 baz: Vec<u32>,
1122 }
1123
1124 fn parse(s: &str) -> Result<u32, <u32 as FromStr>::Err> {
1125 s.parse()
1126 }
1127
1128 is_err!(Opts::parse_args_default(&["x"]),
1129 |e| e.starts_with("invalid argument to option `foo`: "));
1130
1131 is_err!(Opts::parse_args_default(&["0", "x"]),
1132 |e| e.starts_with("invalid argument to option `bar`: "));
1133
1134 is_err!(Opts::parse_args_default(&["0", "0", "x"]),
1135 |e| e.starts_with("invalid argument to option `baz`: "));
1136 }
1137
1138 #[cfg(feature = "default_expr")]
1139 #[test]
test_default_expr()1140 fn test_default_expr() {
1141 #[derive(Options)]
1142 struct Opts {
1143 #[options(default_expr = "foo()")]
1144 foo: u32,
1145 }
1146
1147 fn foo() -> u32 { 123 }
1148
1149 let opts = Opts::parse_args_default(EMPTY).unwrap();
1150 assert_eq!(opts.foo, foo());
1151 }
1152