1mod actions;
2mod builder;
3mod errors;
4mod machine;
5mod node;
6mod runner;
7mod shared;
8mod transition;
9
10pub use builder::*;
11pub use errors::*;
12pub use machine::Machine;
13pub use runner::{OptionValue, Positional, RunState, run_machine, run_partial_machine};
14pub use shared::HELP_COMMAND_INDEX;
15
16#[test]
17fn it_should_select_the_default_command_when_using_no_arguments() {
18 let mut cli_builder = builder::CliBuilder::new();
19
20 cli_builder.add_command()
21 .make_default();
22
23 let machine = cli_builder.compile();
24
25 let result = runner::run_machine(&machine, &vec![]).unwrap();
26 assert_eq!(result.selected_index, Some(0));
27}
28
29#[test]
30fn it_should_select_the_default_command_when_using_mandatory_positional_arguments() {
31 let mut cli_builder = builder::CliBuilder::new();
32
33 cli_builder.add_command()
34 .make_default()
35 .add_positional(true, "foo").unwrap()
36 .add_positional(true, "bar").unwrap();
37
38 let machine = cli_builder.compile();
39
40 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "bar".to_string()]).unwrap();
41 assert_eq!(result.selected_index, Some(0));
42}
43
44#[test]
45fn it_should_select_commands_by_their_path() {
46 let mut cli_builder = builder::CliBuilder::new();
47
48 cli_builder.add_command()
49 .add_path(vec!["foo".to_string()]);
50
51 cli_builder.add_command()
52 .add_path(vec!["bar".to_string()]);
53
54 let machine = cli_builder.compile();
55
56 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
57 assert_eq!(result.selected_index, Some(0));
58
59 let result = runner::run_machine(&machine, &vec!["bar".to_string()]).unwrap();
60 assert_eq!(result.selected_index, Some(1));
61}
62
63#[test]
64fn it_should_select_commands_by_their_required_positional_arguments() {
65 let mut cli_builder = builder::CliBuilder::new();
66
67 cli_builder.add_command()
68 .make_default();
69
70 cli_builder.add_command()
71 .make_default()
72 .add_positional(true, "foo").unwrap();
73
74 let machine = cli_builder.compile();
75
76 let result = runner::run_machine(&machine, &vec![]).unwrap();
77 assert_eq!(result.selected_index, Some(0));
78
79 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
80 assert_eq!(result.selected_index, Some(1));
81}
82
83#[test]
84fn it_should_select_options_by_their_simple_options() {
85 let mut cli_builder = builder::CliBuilder::new();
86
87 cli_builder.add_command()
88 .make_default()
89 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap();
90
91 cli_builder.add_command()
92 .make_default()
93 .add_option(OptionDefinition {name_set: vec!["-y".to_string()], ..Default::default()}).unwrap();
94
95 let machine = cli_builder.compile();
96
97 let result = runner::run_machine(&machine, &vec!["-x".to_string()]).unwrap();
98 assert_eq!(result.selected_index, Some(0));
99
100 let result = runner::run_machine(&machine, &vec!["-y".to_string()]).unwrap();
101 assert_eq!(result.selected_index, Some(1));
102}
103
104#[test]
105fn it_should_allow_options_to_precede_the_command_paths() {
106 let mut cli_builder = builder::CliBuilder::new();
107
108 cli_builder.add_command()
109 .add_path(vec!["foo".to_string()])
110 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap();
111
112 cli_builder.add_command()
113 .add_path(vec!["bar".to_string()])
114 .add_option(OptionDefinition {name_set: vec!["-y".to_string()], ..Default::default()}).unwrap();
115
116 let machine = cli_builder.compile();
117
118 let result = runner::run_machine(&machine, &vec!["-x".to_string(), "foo".to_string()]).unwrap();
119 assert_eq!(result.selected_index, Some(0));
120
121 let result = runner::run_machine(&machine, &vec!["-y".to_string(), "bar".to_string()]).unwrap();
122 assert_eq!(result.selected_index, Some(1));
123}
124
125#[test]
126fn it_should_select_commands_by_their_complex_values() {
127 let mut cli_builder = builder::CliBuilder::new();
128
129 cli_builder.add_command()
130 .make_default()
131 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], arity: 1, ..Default::default()}).unwrap();
132
133 cli_builder.add_command()
134 .make_default()
135 .add_option(OptionDefinition {name_set: vec!["-y".to_string()], arity: 1, ..Default::default()}).unwrap();
136
137 let machine = cli_builder.compile();
138
139 let result = runner::run_machine(&machine, &vec!["-x".to_string(), "foo".to_string()]).unwrap();
140 assert_eq!(result.selected_index, Some(0));
141
142 let result = runner::run_machine(&machine, &vec!["-y".to_string(), "bar".to_string()]).unwrap();
143 assert_eq!(result.selected_index, Some(1));
144}
145
146#[test]
147fn it_should_prefer_longer_paths_over_mandatory_arguments() {
148 let mut cli_builder = builder::CliBuilder::new();
149
150 cli_builder.add_command()
151 .add_path(vec!["foo".to_string()]);
152
153 cli_builder.add_command()
154 .make_default()
155 .add_positional(true, "foo").unwrap();
156
157 let machine = cli_builder.compile();
158
159 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
160 assert_eq!(result.selected_index, Some(0));
161}
162
163#[test]
164fn it_should_prefer_longer_paths_over_mandatory_arguments_reversed() {
165 let mut cli_builder = builder::CliBuilder::new();
166
167 cli_builder.add_command()
168 .make_default()
169 .add_positional(true, "foo").unwrap();
170
171 cli_builder.add_command()
172 .add_path(vec!["foo".to_string()]);
173
174 let machine = cli_builder.compile();
175
176 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
177 assert_eq!(result.selected_index, Some(1));
178}
179
180#[test]
181fn it_should_prefer_longer_paths_over_mandatory_arguments_prefixed() {
182 let mut cli_builder = builder::CliBuilder::new();
183
184 cli_builder.add_command()
185 .add_path(vec!["prfx".to_string(), "foo".to_string()]);
186
187 cli_builder.add_command()
188 .add_path(vec!["prfx".to_string()])
189 .add_positional(true, "foo").unwrap();
190
191 let machine = cli_builder.compile();
192
193 let result = runner::run_machine(&machine, &vec!["prfx".to_string(), "foo".to_string()]).unwrap();
194 assert_eq!(result.selected_index, Some(0));
195}
196
197#[test]
198fn it_should_prefer_longer_paths_over_optional_arguments() {
199 let mut cli_builder = builder::CliBuilder::new();
200
201 cli_builder.add_command()
202 .add_path(vec!["foo".to_string()]);
203
204 cli_builder.add_command()
205 .make_default()
206 .add_positional(false, "foo").unwrap();
207
208 let machine = cli_builder.compile();
209
210 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
211 assert_eq!(result.selected_index, Some(0));
212}
213
214#[test]
215fn it_should_prefer_longer_paths_over_optional_arguments_reversed() {
216 let mut cli_builder = builder::CliBuilder::new();
217
218 cli_builder.add_command()
219 .make_default()
220 .add_positional(false, "foo").unwrap();
221
222 cli_builder.add_command()
223 .add_path(vec!["foo".to_string()]);
224
225 let machine = cli_builder.compile();
226
227 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
228 assert_eq!(result.selected_index, Some(1));
229}
230
231#[test]
232fn it_should_prefer_longer_paths_over_optional_arguments_prefixed() {
233 let mut cli_builder = builder::CliBuilder::new();
234
235 cli_builder.add_command()
236 .add_path(vec!["prfx".to_string(), "foo".to_string()]);
237
238 cli_builder.add_command()
239 .add_path(vec!["prfx".to_string()])
240 .add_positional(false, "foo").unwrap();
241
242 let machine = cli_builder.compile();
243
244 let result = runner::run_machine(&machine, &vec!["prfx".to_string(), "foo".to_string()]).unwrap();
245 assert_eq!(result.selected_index, Some(0));
246}
247
248#[test]
249fn it_should_prefer_required_arguments_over_optional_arguments() {
250 let mut cli_builder = builder::CliBuilder::new();
251
252 cli_builder.add_command()
253 .make_default()
254 .add_positional(true, "foo").unwrap();
255
256 cli_builder.add_command()
257 .make_default()
258 .add_positional(false, "foo").unwrap();
259
260 let machine = cli_builder.compile();
261
262 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
263 assert_eq!(result.selected_index, Some(0));
264}
265
266#[test]
267fn it_should_prefer_required_arguments_over_optional_arguments_reversed() {
268 let mut cli_builder = builder::CliBuilder::new();
269
270 cli_builder.add_command()
271 .make_default()
272 .add_positional(false, "foo").unwrap();
273
274 cli_builder.add_command()
275 .make_default()
276 .add_positional(true, "foo").unwrap();
277
278 let machine = cli_builder.compile();
279
280 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
281 assert_eq!(result.selected_index, Some(1));
282}
283
284#[test]
285fn it_should_fallback_from_path_to_required_arguments_if_needed() {
286 let mut cli_builder = builder::CliBuilder::new();
287
288 cli_builder.add_command()
289 .add_path(vec!["foo".to_string()]);
290
291 cli_builder.add_command()
292 .make_default()
293 .add_positional(true, "foo").unwrap();
294
295 let machine = cli_builder.compile();
296
297 let result = runner::run_machine(&machine, &vec!["bar".to_string()]).unwrap();
298 assert_eq!(result.selected_index, Some(1));
299}
300
301#[test]
302fn it_should_fallback_from_path_to_required_arguments_if_needed_reverse() {
303 let mut cli_builder = builder::CliBuilder::new();
304
305 cli_builder.add_command()
306 .make_default()
307 .add_positional(true, "foo").unwrap();
308
309 cli_builder.add_command()
310 .add_path(vec!["foo".to_string()]);
311
312 let machine = cli_builder.compile();
313
314 let result = runner::run_machine(&machine, &vec!["bar".to_string()]).unwrap();
315 assert_eq!(result.selected_index, Some(0));
316}
317
318#[test]
319fn it_should_fallback_from_path_to_required_arguments_if_needed_prefixed() {
320 let mut cli_builder = builder::CliBuilder::new();
321
322 cli_builder.add_command()
323 .add_path(vec!["prfx".to_string(), "foo".to_string()]);
324
325 cli_builder.add_command()
326 .add_path(vec!["prfx".to_string()])
327 .add_positional(true, "foo").unwrap();
328
329 let machine = cli_builder.compile();
330
331 let result = runner::run_machine(&machine, &vec!["prfx".to_string(), "bar".to_string()]).unwrap();
332 assert_eq!(result.selected_index, Some(1));
333}
334
335#[test]
336fn it_should_fallback_from_path_to_optional_arguments_if_needed() {
337 let mut cli_builder = builder::CliBuilder::new();
338
339 cli_builder.add_command()
340 .add_path(vec!["foo".to_string()]);
341
342 cli_builder.add_command()
343 .make_default()
344 .add_positional(false, "foo").unwrap();
345
346 let machine = cli_builder.compile();
347
348 let result = runner::run_machine(&machine, &vec!["bar".to_string()]).unwrap();
349 assert_eq!(result.selected_index, Some(1));
350}
351
352#[test]
353fn it_should_fallback_from_path_to_optional_arguments_if_needed_reverse() {
354 let mut cli_builder = builder::CliBuilder::new();
355
356 cli_builder.add_command()
357 .make_default()
358 .add_positional(false, "foo").unwrap();
359
360 cli_builder.add_command()
361 .add_path(vec!["foo".to_string()]);
362
363 let machine = cli_builder.compile();
364
365 let result = runner::run_machine(&machine, &vec!["bar".to_string()]).unwrap();
366 assert_eq!(result.selected_index, Some(0));
367}
368
369#[test]
370fn it_should_fallback_from_path_to_optional_arguments_if_needed_prefixed() {
371 let mut cli_builder = builder::CliBuilder::new();
372
373 cli_builder.add_command()
374 .add_path(vec!["prfx".to_string(), "foo".to_string()]);
375
376 cli_builder.add_command()
377 .add_path(vec!["prfx".to_string()])
378 .add_positional(false, "foo").unwrap();
379
380 let machine = cli_builder.compile();
381
382 let result = runner::run_machine(&machine, &vec!["prfx".to_string(), "bar".to_string()]).unwrap();
383 assert_eq!(result.selected_index, Some(1));
384}
385
386#[test]
387fn it_should_extract_booleans_from_simple_options() {
388 let mut cli_builder = builder::CliBuilder::new();
389
390 cli_builder.add_command()
391 .make_default()
392 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap();
393
394 let machine = cli_builder.compile();
395
396 let result = runner::run_machine(&machine, &vec!["-x".to_string()]).unwrap();
397 assert_eq!(result.options, vec![("-x".to_string(), runner::OptionValue::Bool(true))]);
398}
399
400#[test]
401fn it_should_extract_booleans_from_batch_options() {
402 let mut cli_builder = builder::CliBuilder::new();
403
404 cli_builder.add_command()
405 .make_default()
406 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap()
407 .add_option(OptionDefinition {name_set: vec!["-y".to_string()], ..Default::default()}).unwrap();
408
409 let machine = cli_builder.compile();
410
411 let result = runner::run_machine(&machine, &vec!["-xy".to_string()]).unwrap();
412 assert_eq!(result.options, vec![
413 ("-x".to_string(), runner::OptionValue::Bool(true)),
414 ("-y".to_string(), runner::OptionValue::Bool(true)),
415 ]);
416}
417
418#[test]
419fn it_should_invert_booleans_when_using_no() {
420 let mut cli_builder = builder::CliBuilder::new();
421
422 cli_builder.add_command()
423 .make_default()
424 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], ..Default::default()}).unwrap();
425
426 let machine = cli_builder.compile();
427
428 let result = runner::run_machine(&machine, &vec!["--no-foo".to_string()]).unwrap();
429 assert_eq!(result.options, vec![("--foo".to_string(), runner::OptionValue::Bool(false))]);
430}
431
432#[test]
433fn it_should_extract_strings_from_complex_options() {
434 let mut cli_builder = builder::CliBuilder::new();
435
436 cli_builder.add_command()
437 .make_default()
438 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], arity: 1, ..Default::default()}).unwrap();
439
440 let machine = cli_builder.compile();
441
442 let result = runner::run_machine(&machine, &vec!["-x".to_string(), "foo".to_string()]).unwrap();
443 assert_eq!(result.options, vec![("-x".to_string(), runner::OptionValue::String("foo".to_string()))]);
444}
445
446#[test]
447fn it_should_extract_strings_from_complex_options_with_equals() {
448 let mut cli_builder = builder::CliBuilder::new();
449
450 cli_builder.add_command()
451 .make_default()
452 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], arity: 1, ..Default::default()}).unwrap();
453
454 let machine = cli_builder.compile();
455
456 let result = runner::run_machine(&machine, &vec!["--foo=foo".to_string()]).unwrap();
457 assert_eq!(result.options, vec![("--foo".to_string(), runner::OptionValue::String("foo".to_string()))]);
458}
459
460#[test]
461fn it_shouldnt_consider_dash_as_an_option() {
462 let mut cli_builder = builder::CliBuilder::new();
463
464 cli_builder.add_command()
465 .make_default()
466 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], arity: 1, ..Default::default()}).unwrap();
467
468 let machine = cli_builder.compile();
469
470 let result = runner::run_machine(&machine, &vec!["--foo".to_string(), "-".to_string()]).unwrap();
471 assert_eq!(result.options, vec![("--foo".to_string(), runner::OptionValue::String("-".to_string()))]);
472}
473
474#[test]
475fn it_should_extract_arrays_from_complex_options() {
476 let mut cli_builder = builder::CliBuilder::new();
477
478 cli_builder.add_command()
479 .make_default()
480 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], arity: 1, ..Default::default()}).unwrap();
481
482 let machine = cli_builder.compile();
483
484 let result = runner::run_machine(&machine, &vec!["--foo".to_string(), "bar".to_string(), "--foo".to_string(), "baz".to_string()]).unwrap();
485 assert_eq!(result.options, vec![
486 ("--foo".to_string(), runner::OptionValue::String("bar".to_string())),
487 ("--foo".to_string(), runner::OptionValue::String("baz".to_string())),
488 ]);
489}
490
491#[test]
492fn it_should_extract_arrays_from_complex_options_equal() {
493 let mut cli_builder = builder::CliBuilder::new();
494
495 cli_builder.add_command()
496 .make_default()
497 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], arity: 1, ..Default::default()}).unwrap();
498
499 let machine = cli_builder.compile();
500
501 let result = runner::run_machine(&machine, &vec!["--foo=bar".to_string(), "--foo=baz".to_string()]).unwrap();
502 assert_eq!(result.options, vec![
503 ("--foo".to_string(), runner::OptionValue::String("bar".to_string())),
504 ("--foo".to_string(), runner::OptionValue::String("baz".to_string())),
505 ]);
506}
507
508#[test]
509fn it_should_extract_arrays_from_complex_options_mixed() {
510 let mut cli_builder = builder::CliBuilder::new();
511
512 cli_builder.add_command()
513 .make_default()
514 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], arity: 1, ..Default::default()}).unwrap();
515
516 let machine = cli_builder.compile();
517
518 let result = runner::run_machine(&machine, &vec!["--foo".to_string(), "bar".to_string(), "--foo=baz".to_string()]).unwrap();
519 assert_eq!(result.options, vec![
520 ("--foo".to_string(), runner::OptionValue::String("bar".to_string())),
521 ("--foo".to_string(), runner::OptionValue::String("baz".to_string())),
522 ]);
523}
524
525#[test]
526fn it_should_support_rest_arguments() {
527 let mut cli_builder = builder::CliBuilder::new();
528
529 cli_builder.add_command()
530 .make_default()
531 .add_rest("rest").unwrap();
532
533 let machine = cli_builder.compile();
534
535 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]).unwrap();
536 assert_eq!(result.positionals, vec![
537 runner::Positional::Rest("foo".to_string()),
538 runner::Positional::Rest("bar".to_string()),
539 runner::Positional::Rest("baz".to_string()),
540 ]);
541}
542
543#[test]
544fn it_should_support_rest_arguments_followed_by_mandatory_arguments() {
545 let mut cli_builder = builder::CliBuilder::new();
546
547 cli_builder.add_command()
548 .make_default()
549 .add_rest("rest").unwrap()
550 .add_positional(true, "foo").unwrap();
551
552 let machine = cli_builder.compile();
553
554 let result = runner::run_machine(&machine, &vec!["src1".to_string(), "src2".to_string(), "src3".to_string(), "dest".to_string()]).unwrap();
555 assert_eq!(result.positionals, vec![
556 runner::Positional::Rest("src1".to_string()),
557 runner::Positional::Rest("src2".to_string()),
558 runner::Positional::Rest("src3".to_string()),
559 runner::Positional::Required("dest".to_string()),
560 ]);
561}
562
563#[test]
564fn it_should_support_rest_arguments_between_mandatory_arguments() {
565 let mut cli_builder = builder::CliBuilder::new();
566
567 cli_builder.add_command()
568 .make_default()
569 .add_positional(true, "foo").unwrap()
570 .add_rest("rest").unwrap()
571 .add_positional(true, "bar").unwrap();
572
573 let machine = cli_builder.compile();
574
575 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "src1".to_string(), "src2".to_string(), "src3".to_string(), "dest".to_string()]).unwrap();
576 assert_eq!(result.positionals, vec![
577 runner::Positional::Required("foo".to_string()),
578 runner::Positional::Rest("src1".to_string()),
579 runner::Positional::Rest("src2".to_string()),
580 runner::Positional::Rest("src3".to_string()),
581 runner::Positional::Required("dest".to_string()),
582 ]);
583}
584
585#[test]
586fn it_should_support_option_arguments_in_between_rest_arguments() {
587 let mut cli_builder = builder::CliBuilder::new();
588
589 cli_builder.add_command()
590 .make_default()
591 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], ..Default::default()}).unwrap()
592 .add_option(OptionDefinition {name_set: vec!["--bar".to_string()], arity: 1, ..Default::default()}).unwrap()
593 .add_rest("rest").unwrap();
594
595 let machine = cli_builder.compile();
596
597 let result = runner::run_machine(&machine, &vec!["src1".to_string(), "--foo".to_string(), "src2".to_string(), "--bar".to_string(), "baz".to_string(), "src3".to_string()]).unwrap();
598
599 assert_eq!(result.options, vec![
600 ("--foo".to_string(), runner::OptionValue::Bool(true)),
601 ("--bar".to_string(), runner::OptionValue::String("baz".to_string())),
602 ]);
603
604 assert_eq!(result.positionals, vec![
605 runner::Positional::Rest("src1".to_string()),
606 runner::Positional::Rest("src2".to_string()),
607 runner::Positional::Rest("src3".to_string()),
608 ]);
609}
610
611#[test]
612fn it_should_ignore_options_when_they_follow_the_dash_dash_separator() {
613 let mut cli_builder = builder::CliBuilder::new();
614
615 cli_builder.add_command()
616 .add_path(vec!["foo".to_string()])
617 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap()
618 .add_positional(false, "foo").unwrap();
619
620 let machine = cli_builder.compile();
621
622 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "--".to_string(), "-x".to_string()]).unwrap();
623
624 assert_eq!(result.options, vec![
625 ]);
627
628 assert_eq!(result.positionals, vec![
629 runner::Positional::Optional("-x".to_string()),
630 ]);
631}
632
633#[test]
634fn it_should_ignore_options_when_they_appear_after_a_required_positional_from_a_proxy() {
635 let mut cli_builder = builder::CliBuilder::new();
636
637 cli_builder.add_command()
638 .add_path(vec!["foo".to_string()])
639 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap()
640 .add_positional(true, "foo").unwrap()
641 .add_positional(true, "bar").unwrap()
642 .add_proxy("proxy").unwrap();
643
644 let machine = cli_builder.compile();
645
646 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "pos1".to_string(), "-x".to_string(), "pos2".to_string(), "proxy".to_string()]).unwrap();
647
648 assert_eq!(result.options, vec![
649 ("-x".to_string(), runner::OptionValue::Bool(true)),
650 ]);
651
652 assert_eq!(result.positionals, vec![
653 runner::Positional::Required("pos1".to_string()),
654 runner::Positional::Required("pos2".to_string()),
655 runner::Positional::Rest("proxy".to_string()),
656 ]);
657}
658
659#[test]
660fn it_should_ignore_options_when_they_appear_in_a_proxy_extra() {
661 let mut cli_builder = builder::CliBuilder::new();
662
663 cli_builder.add_command()
664 .add_path(vec!["foo".to_string()])
665 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap()
666 .add_proxy("proxy").unwrap();
667
668 let machine = cli_builder.compile();
669
670 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "-x".to_string()]).unwrap();
671
672 assert_eq!(result.options, vec![
673 ]);
675
676 assert_eq!(result.positionals, vec![
677 runner::Positional::Rest("-x".to_string()),
678 ]);
679}
680
681#[test]
682fn it_should_prefer_exact_commands_over_empty_proxies() {
683 let mut cli_builder = builder::CliBuilder::new();
684
685 cli_builder.add_command()
686 .add_path(vec!["foo".to_string()]);
687
688 cli_builder.add_command()
689 .add_path(vec!["foo".to_string()])
690 .add_positional(true, "foo").unwrap()
691 .add_proxy("proxy").unwrap();
692
693 let machine = cli_builder.compile();
694
695 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
696 assert_eq!(result.selected_index, Some(0));
697}
698
699#[test]
700fn it_should_aggregate_the_options_as_they_are_found() {
701 let mut cli_builder = builder::CliBuilder::new();
702
703 cli_builder.add_command()
704 .make_default()
705 .add_option(OptionDefinition {name_set: vec!["-x".to_string()], ..Default::default()}).unwrap()
706 .add_option(OptionDefinition {name_set: vec!["-y".to_string()], ..Default::default()}).unwrap()
707 .add_option(OptionDefinition {name_set: vec!["-z".to_string()], ..Default::default()}).unwrap()
708 .add_option(OptionDefinition {name_set: vec!["-u".to_string()], arity: 1, ..Default::default()}).unwrap()
709 .add_option(OptionDefinition {name_set: vec!["-v".to_string()], arity: 1, ..Default::default()}).unwrap()
710 .add_option(OptionDefinition {name_set: vec!["-w".to_string()], arity: 1, ..Default::default()}).unwrap();
711
712 let machine = cli_builder.compile();
713
714 let result = runner::run_machine(&machine, &vec!["-x".to_string(), "-u".to_string(), "foo".to_string(), "-y".to_string(), "-v".to_string(), "bar".to_string(), "-y".to_string()]).unwrap();
715 assert_eq!(result.options, vec![
716 ("-x".to_string(), runner::OptionValue::Bool(true)),
717 ("-u".to_string(), runner::OptionValue::String("foo".to_string())),
718 ("-y".to_string(), runner::OptionValue::Bool(true)),
719 ("-v".to_string(), runner::OptionValue::String("bar".to_string())),
720 ("-y".to_string(), runner::OptionValue::Bool(true)),
721 ]);
722
723 let result = runner::run_machine(&machine, &vec!["-z".to_string(), "-y".to_string(), "-x".to_string()]).unwrap();
724 assert_eq!(result.options, vec![
725 ("-z".to_string(), runner::OptionValue::Bool(true)),
726 ("-y".to_string(), runner::OptionValue::Bool(true)),
727 ("-x".to_string(), runner::OptionValue::Bool(true)),
728 ]);
729}
730
731#[test]
732fn it_should_aggregate_the_mandatory_arguments() {
733 let mut cli_builder = builder::CliBuilder::new();
734
735 cli_builder.add_command()
736 .make_default()
737 .add_positional(true, "foo").unwrap()
738 .add_positional(true, "bar").unwrap();
739
740 let machine = cli_builder.compile();
741
742 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "bar".to_string()]).unwrap();
743 assert_eq!(result.positionals, vec![
744 runner::Positional::Required("foo".to_string()),
745 runner::Positional::Required("bar".to_string()),
746 ]);
747}
748
749#[test]
750fn it_should_aggregate_the_optional_arguments() {
751 let mut cli_builder = builder::CliBuilder::new();
752
753 cli_builder.add_command()
754 .make_default()
755 .add_positional(false, "foo").unwrap()
756 .add_positional(false, "bar").unwrap();
757
758 let machine = cli_builder.compile();
759
760 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "bar".to_string()]).unwrap();
761 assert_eq!(result.positionals, vec![
762 runner::Positional::Optional("foo".to_string()),
763 runner::Positional::Optional("bar".to_string()),
764 ]);
765}
766
767#[test]
768fn it_should_accept_as_few_optional_arguments_as_possible() {
769 let mut cli_builder = builder::CliBuilder::new();
770
771 cli_builder.add_command()
772 .make_default()
773 .add_positional(false, "foo").unwrap()
774 .add_positional(false, "bar").unwrap();
775
776 let machine = cli_builder.compile();
777
778 let result = runner::run_machine(&machine, &vec![]).unwrap();
779 assert_eq!(result.positionals, vec![]);
780
781 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
782 assert_eq!(result.positionals, vec![
783 runner::Positional::Optional("foo".to_string()),
784 ]);
785}
786
787#[test]
788fn it_should_accept_a_mix_of_mandatory_and_optional_arguments() {
789 let mut cli_builder = builder::CliBuilder::new();
790
791 cli_builder.add_command()
792 .make_default()
793 .add_positional(true, "foo").unwrap()
794 .add_positional(false, "bar").unwrap();
795
796 let machine = cli_builder.compile();
797
798 let result = runner::run_machine(&machine, &vec!["foo".to_string()]).unwrap();
799 assert_eq!(result.positionals, vec![
800 runner::Positional::Required("foo".to_string()),
801 ]);
802
803 let result = runner::run_machine(&machine, &vec!["foo".to_string(), "bar".to_string()]).unwrap();
804 assert_eq!(result.positionals, vec![
805 runner::Positional::Required("foo".to_string()),
806 runner::Positional::Optional("bar".to_string()),
807 ]);
808}
809
810#[test]
811fn it_should_accept_any_option_as_positional_argument_when_proxies_are_enabled() {
812 let mut cli_builder = builder::CliBuilder::new();
813
814 cli_builder.add_command()
815 .make_default()
816 .add_proxy("proxy").unwrap();
817
818 let machine = cli_builder.compile();
819
820 let result = runner::run_machine(&machine, &vec!["--foo".to_string(), "--bar".to_string()]).unwrap();
821 assert_eq!(result.positionals, vec![
822 runner::Positional::Rest("--foo".to_string()),
823 runner::Positional::Rest("--bar".to_string()),
824 ]);
825}
826
827#[test]
828fn it_should_throw_acceptable_errors_when_passing_an_extraneous_option() {
829 let mut cli_builder = builder::CliBuilder::new();
830
831 cli_builder.add_command()
832 .make_default();
833
834 let machine = cli_builder.compile();
835
836 let result = runner::run_machine(&machine, &vec!["--foo".to_string()]);
837 assert!(matches!(result, Err(errors::Error::CommandError(_, errors::CommandError::UnknownOption))));
838}
839
840#[test]
841fn it_should_throw_acceptable_errors_when_passing_extraneous_arguments() {
842 let mut cli_builder = builder::CliBuilder::new();
843
844 cli_builder.add_command()
845 .make_default();
846
847 let machine = cli_builder.compile();
848
849 let result = runner::run_machine(&machine, &vec!["foo".to_string()]);
850 assert!(matches!(result, Err(errors::Error::CommandError(_, errors::CommandError::ExtraneousPositionalArguments))));
851}
852
853#[test]
854fn it_should_throw_acceptable_errors_when_writing_invalid_arguments() {
855 let mut cli_builder = builder::CliBuilder::new();
856
857 cli_builder.add_command()
858 .make_default();
859
860 let machine = cli_builder.compile();
861
862 let result = runner::run_machine(&machine, &vec!["-%#@$%#()@".to_string()]);
863 assert!(matches!(result, Err(errors::Error::CommandError(_, errors::CommandError::InvalidOption))));
864}
865
866#[test]
867fn it_should_throw_acceptable_errors_when_writing_bound_boolean_arguments() {
868 let mut cli_builder = builder::CliBuilder::new();
869
870 cli_builder.add_command()
871 .make_default()
872 .add_option(OptionDefinition {name_set: vec!["--foo".to_string()], allow_binding: false, ..Default::default()}).unwrap();
873
874 let machine = cli_builder.compile();
875
876 let result = runner::run_machine(&machine, &vec!["--foo=bar".to_string()]);
877 assert!(matches!(result, Err(errors::Error::CommandError(_, errors::CommandError::InvalidOption))));
878}
879
880#[test]
881fn it_should_rename_truthy_options_into_their_preferred_name() {
882 let mut cli_builder = builder::CliBuilder::new();
883
884 cli_builder.add_command()
885 .make_default()
886 .add_option(OptionDefinition {name_set: vec!["--value".to_string(), "-v".to_string()], ..Default::default()}).unwrap();
887
888 let machine = cli_builder.compile();
889
890 let result = runner::run_machine(&machine, &vec!["-v".to_string()]).unwrap();
891 assert_eq!(result.options, vec![("--value".to_string(), runner::OptionValue::Bool(true))]);
892}
893
894#[test]
895fn it_should_rename_falsy_options_into_their_preferred_name() {
896 let mut cli_builder = builder::CliBuilder::new();
897
898 cli_builder.add_command()
899 .make_default()
900 .add_option(OptionDefinition {name_set: vec!["--value".to_string(), "--v".to_string()], ..Default::default()}).unwrap();
901
902 let machine = cli_builder.compile();
903
904 let result = runner::run_machine(&machine, &vec!["--no-v".to_string()]).unwrap();
905 assert_eq!(result.options, vec![("--value".to_string(), runner::OptionValue::Bool(false))]);
906}
907
908#[test]
909fn it_should_rename_batch_options_into_their_preferred_name() {
910 let mut cli_builder = builder::CliBuilder::new();
911
912 cli_builder.add_command()
913 .make_default()
914 .add_option(OptionDefinition {name_set: vec!["--foo".to_string(), "-f".to_string()], ..Default::default()}).unwrap()
915 .add_option(OptionDefinition {name_set: vec!["--bar".to_string(), "-b".to_string()], ..Default::default()}).unwrap();
916
917 let machine = cli_builder.compile();
918
919 let result = runner::run_machine(&machine, &vec!["-fb".to_string()]).unwrap();
920 assert_eq!(result.options, vec![
921 ("--foo".to_string(), runner::OptionValue::Bool(true)),
922 ("--bar".to_string(), runner::OptionValue::Bool(true)),
923 ]);
924}
925
926#[test]
927fn it_should_rename_string_options_into_their_preferred_name() {
928 let mut cli_builder = builder::CliBuilder::new();
929
930 cli_builder.add_command()
931 .make_default()
932 .add_option(OptionDefinition {name_set: vec!["--value".to_string(), "-v".to_string()], arity: 1, ..Default::default()}).unwrap();
933
934 let machine = cli_builder.compile();
935
936 let result = runner::run_machine(&machine, &vec!["-v".to_string(), "foo".to_string()]).unwrap();
937 assert_eq!(result.options, vec![("--value".to_string(), runner::OptionValue::String("foo".to_string()))]);
938}
939
940#[test]
941fn it_should_rename_array_options_into_their_preferred_name() {
942 let mut cli_builder = builder::CliBuilder::new();
943
944 cli_builder.add_command()
945 .make_default()
946 .add_option(OptionDefinition {name_set: vec!["--value".to_string(), "-v".to_string()], arity: 2, ..Default::default()}).unwrap();
947
948 let machine = cli_builder.compile();
949
950 let result = runner::run_machine(&machine, &vec!["-v".to_string(), "foo".to_string(), "bar".to_string()]).unwrap();
951 assert_eq!(result.options, vec![
952 ("--value".to_string(), runner::OptionValue::Array(vec!["foo".to_string(), "bar".to_string()])),
953 ]);
954}