1use super::utils;
2
3use std::io::Write;
4use std::str;
5
6use clap::{Arg, Command};
7
8static SCF2OP: &str = "flag present 2 times
9option NOT present
10positional NOT present
11flag2 NOT present
12option2 maybe present with value of: Nothing
13positional2 maybe present with value of: Nothing
14option3 NOT present
15positional3 NOT present
16option NOT present
17positional NOT present
18subcmd present
19flag present 2 times
20scoption present with value: some
21An scoption: some
22scpositional present with value: value
23";
24
25static SCFOP: &str = "flag present 1 times
26option NOT present
27positional NOT present
28flag2 NOT present
29option2 maybe present with value of: Nothing
30positional2 maybe present with value of: Nothing
31option3 NOT present
32positional3 NOT present
33option NOT present
34positional NOT present
35subcmd present
36flag present 1 times
37scoption present with value: some
38An scoption: some
39scpositional present with value: value
40";
41
42static O2P: &str = "flag NOT present
43option present with value: some
44An option: some
45An option: other
46positional present with value: value
47flag2 NOT present
48option2 maybe present with value of: Nothing
49positional2 maybe present with value of: Nothing
50option3 NOT present
51positional3 NOT present
52option present with value: some
53An option: some
54An option: other
55positional present with value: value
56subcmd NOT present
57";
58
59static F2OP: &str = "flag present 2 times
60option present with value: some
61An option: some
62positional present with value: value
63flag2 NOT present
64option2 maybe present with value of: Nothing
65positional2 maybe present with value of: Nothing
66option3 NOT present
67positional3 NOT present
68option present with value: some
69An option: some
70positional present with value: value
71subcmd NOT present
72";
73
74static FOP: &str = "flag present 1 times
75option present with value: some
76An option: some
77positional present with value: value
78flag2 NOT present
79option2 maybe present with value of: Nothing
80positional2 maybe present with value of: Nothing
81option3 NOT present
82positional3 NOT present
83option present with value: some
84An option: some
85positional present with value: value
86subcmd NOT present
87";
88
89pub fn check_complex_output(args: &str, out: &str) {
90    let mut w = vec![];
91    let matches = utils::complex_app()
92        .try_get_matches_from(args.split(' ').collect::<Vec<_>>())
93        .unwrap();
94    match matches.get_one::<u8>("flag").unwrap() {
95        0 => {
96            writeln!(w, "flag NOT present").unwrap();
97        }
98        n => {
99            writeln!(w, "flag present {} times", n).unwrap();
100        }
101    }
102
103    if matches.contains_id("option") {
104        if let Some(v) = matches.get_one::<String>("option").map(|v| v.as_str()) {
105            writeln!(w, "option present with value: {}", v).unwrap();
106        }
107        if let Some(ov) = matches.get_many::<String>("option") {
108            for o in ov {
109                writeln!(w, "An option: {}", o).unwrap();
110            }
111        }
112    } else {
113        writeln!(w, "option NOT present").unwrap();
114    }
115
116    if let Some(p) = matches.get_one::<String>("positional").map(|v| v.as_str()) {
117        writeln!(w, "positional present with value: {}", p).unwrap();
118    } else {
119        writeln!(w, "positional NOT present").unwrap();
120    }
121
122    if *matches.get_one::<bool>("flag2").expect("defaulted by clap") {
123        writeln!(w, "flag2 present").unwrap();
124        writeln!(
125            w,
126            "option2 present with value of: {}",
127            matches
128                .get_one::<String>("long-option-2")
129                .map(|v| v.as_str())
130                .unwrap()
131        )
132        .unwrap();
133        writeln!(
134            w,
135            "positional2 present with value of: {}",
136            matches
137                .get_one::<String>("positional2")
138                .map(|v| v.as_str())
139                .unwrap()
140        )
141        .unwrap();
142    } else {
143        writeln!(w, "flag2 NOT present").unwrap();
144        writeln!(
145            w,
146            "option2 maybe present with value of: {}",
147            matches
148                .get_one::<String>("long-option-2")
149                .map(|v| v.as_str())
150                .unwrap_or("Nothing")
151        )
152        .unwrap();
153        writeln!(
154            w,
155            "positional2 maybe present with value of: {}",
156            matches
157                .get_one::<String>("positional2")
158                .map(|v| v.as_str())
159                .unwrap_or("Nothing")
160        )
161        .unwrap();
162    }
163
164    let _ = match matches
165        .get_one::<String>("option3")
166        .map(|v| v.as_str())
167        .unwrap_or("")
168    {
169        "fast" => writeln!(w, "option3 present quickly"),
170        "slow" => writeln!(w, "option3 present slowly"),
171        _ => writeln!(w, "option3 NOT present"),
172    };
173
174    let _ = match matches
175        .get_one::<String>("positional3")
176        .map(|v| v.as_str())
177        .unwrap_or("")
178    {
179        "vi" => writeln!(w, "positional3 present in vi mode"),
180        "emacs" => writeln!(w, "positional3 present in emacs mode"),
181        _ => writeln!(w, "positional3 NOT present"),
182    };
183
184    if matches.contains_id("option") {
185        if let Some(v) = matches.get_one::<String>("option").map(|v| v.as_str()) {
186            writeln!(w, "option present with value: {}", v).unwrap();
187        }
188        if let Some(ov) = matches.get_many::<String>("option") {
189            for o in ov {
190                writeln!(w, "An option: {}", o).unwrap();
191            }
192        }
193    } else {
194        writeln!(w, "option NOT present").unwrap();
195    }
196
197    if let Some(p) = matches.get_one::<String>("positional").map(|v| v.as_str()) {
198        writeln!(w, "positional present with value: {p}").unwrap();
199    } else {
200        writeln!(w, "positional NOT present").unwrap();
201    }
202    if let Some("subcmd") = matches.subcommand_name() {
203        writeln!(w, "subcmd present").unwrap();
204        if let Some(matches) = matches.subcommand_matches("subcmd") {
205            match matches.get_one::<u8>("flag").unwrap() {
206                0 => {
207                    writeln!(w, "flag NOT present").unwrap();
208                }
209                n => {
210                    writeln!(w, "flag present {n} times").unwrap();
211                }
212            }
213
214            if matches.contains_id("option") {
215                if let Some(v) = matches.get_one::<String>("option").map(|v| v.as_str()) {
216                    writeln!(w, "scoption present with value: {v}").unwrap();
217                }
218                if let Some(ov) = matches.get_many::<String>("option") {
219                    for o in ov {
220                        writeln!(w, "An scoption: {o}").unwrap();
221                    }
222                }
223            } else {
224                writeln!(w, "scoption NOT present").unwrap();
225            }
226
227            if let Some(p) = matches
228                .get_one::<String>("scpositional")
229                .map(|v| v.as_str())
230            {
231                writeln!(w, "scpositional present with value: {p}").unwrap();
232            }
233        }
234    } else {
235        writeln!(w, "subcmd NOT present").unwrap();
236    }
237
238    let res = str::from_utf8(&w).unwrap();
239    snapbox::assert_eq(out, res);
240}
241
242#[test]
243fn create_app() {
244    let _ = Command::new("test")
245        .version("1.0")
246        .author("kevin")
247        .about("does awesome things")
248        .try_get_matches_from(vec![""])
249        .unwrap();
250}
251
252#[test]
253fn add_multiple_arg() {
254    let _ = Command::new("test")
255        .args([Arg::new("test").short('s'), Arg::new("test2").short('l')])
256        .try_get_matches_from(vec![""])
257        .unwrap();
258}
259#[test]
260fn flag_x2_opt() {
261    check_complex_output(
262        "clap-test value -f -f -o some",
263        "flag present 2 times
264option present with value: some
265An option: some
266positional present with value: value
267flag2 NOT present
268option2 maybe present with value of: Nothing
269positional2 maybe present with value of: Nothing
270option3 NOT present
271positional3 NOT present
272option present with value: some
273An option: some
274positional present with value: value
275subcmd NOT present
276",
277    );
278}
279
280#[test]
281fn long_opt_x2_pos() {
282    check_complex_output("clap-test value --option some --option other", O2P);
283}
284
285#[test]
286fn long_opt_eq_x2_pos() {
287    check_complex_output("clap-test value --option=some --option=other", O2P);
288}
289
290#[test]
291fn short_opt_x2_pos() {
292    check_complex_output("clap-test value -o some -o other", O2P);
293}
294
295#[test]
296fn short_opt_eq_x2_pos() {
297    check_complex_output("clap-test value -o=some -o=other", O2P);
298}
299
300#[test]
301fn short_flag_x2_comb_short_opt_pos() {
302    check_complex_output("clap-test value -ff -o some", F2OP);
303}
304
305#[test]
306fn short_flag_short_opt_pos() {
307    check_complex_output("clap-test value -f -o some", FOP);
308}
309
310#[test]
311fn long_flag_long_opt_pos() {
312    check_complex_output("clap-test value --flag --option some", FOP);
313}
314
315#[test]
316fn long_flag_long_opt_eq_pos() {
317    check_complex_output("clap-test value --flag --option=some", FOP);
318}
319
320#[test]
321fn sc_long_flag_long_opt() {
322    check_complex_output("clap-test subcmd value --flag --option some", SCFOP);
323}
324
325#[test]
326fn sc_long_flag_short_opt_pos() {
327    check_complex_output("clap-test subcmd value --flag -o some", SCFOP);
328}
329
330#[test]
331fn sc_long_flag_long_opt_eq_pos() {
332    check_complex_output("clap-test subcmd value --flag --option=some", SCFOP);
333}
334
335#[test]
336fn sc_short_flag_long_opt_pos() {
337    check_complex_output("clap-test subcmd value -f --option some", SCFOP);
338}
339
340#[test]
341fn sc_short_flag_short_opt_pos() {
342    check_complex_output("clap-test subcmd value -f -o some", SCFOP);
343}
344
345#[test]
346fn sc_short_flag_short_opt_eq_pos() {
347    check_complex_output("clap-test subcmd value -f -o=some", SCFOP);
348}
349
350#[test]
351fn sc_short_flag_long_opt_eq_pos() {
352    check_complex_output("clap-test subcmd value -f --option=some", SCFOP);
353}
354
355#[test]
356fn sc_short_flag_x2_comb_long_opt_pos() {
357    check_complex_output("clap-test subcmd value -ff --option some", SCF2OP);
358}
359
360#[test]
361fn sc_short_flag_x2_comb_short_opt_pos() {
362    check_complex_output("clap-test subcmd value -ff -o some", SCF2OP);
363}
364
365#[test]
366fn sc_short_flag_x2_comb_long_opt_eq_pos() {
367    check_complex_output("clap-test subcmd value -ff --option=some", SCF2OP);
368}
369
370#[test]
371fn sc_short_flag_x2_comb_short_opt_eq_pos() {
372    check_complex_output("clap-test subcmd value -ff -o=some", SCF2OP);
373}
374
375#[test]
376fn sc_long_flag_x2_long_opt_pos() {
377    check_complex_output("clap-test subcmd value --flag --flag --option some", SCF2OP);
378}
379
380#[test]
381fn sc_long_flag_x2_short_opt_pos() {
382    check_complex_output("clap-test subcmd value --flag --flag -o some", SCF2OP);
383}
384
385#[test]
386fn sc_long_flag_x2_short_opt_eq_pos() {
387    check_complex_output("clap-test subcmd value --flag --flag -o=some", SCF2OP);
388}
389
390#[test]
391fn sc_long_flag_x2_long_opt_eq_pos() {
392    check_complex_output("clap-test subcmd value --flag --flag --option=some", SCF2OP);
393}
394
395#[test]
396fn sc_short_flag_x2_long_opt_pos() {
397    check_complex_output("clap-test subcmd value -f -f --option some", SCF2OP);
398}
399
400#[test]
401fn sc_short_flag_x2_short_opt_pos() {
402    check_complex_output("clap-test subcmd value -f -f -o some", SCF2OP);
403}
404
405#[test]
406fn sc_short_flag_x2_short_opt_eq_pos() {
407    check_complex_output("clap-test subcmd value -f -f -o=some", SCF2OP);
408}
409
410#[test]
411fn sc_short_flag_x2_long_opt_eq_pos() {
412    check_complex_output("clap-test subcmd value -f -f --option=some", SCF2OP);
413}
414
415#[test]
416fn mut_arg_all() {
417    let mut cmd = utils::complex_app();
418    let arg_names = cmd
419        .get_arguments()
420        .map(|a| a.get_id().clone())
421        .filter(|a| a != "version" && a != "help")
422        .collect::<Vec<_>>();
423
424    for arg_name in arg_names {
425        cmd = cmd.mut_arg(arg_name, |arg| arg.hide_possible_values(true));
426    }
427}
428
429#[test]
430fn mut_subcommand_all() {
431    let cmd = utils::complex_app();
432
433    assert_eq!(
434        cmd.find_subcommand("subcmd")
435            .unwrap()
436            .is_disable_version_flag_set(),
437        false
438    );
439    let cmd = cmd.mut_subcommand("subcmd", |subcmd| subcmd.disable_version_flag(true));
440    assert_eq!(
441        cmd.find_subcommand("subcmd")
442            .unwrap()
443            .is_disable_version_flag_set(),
444        true
445    );
446}
447
448#[test]
449fn mut_subcommand_with_alias_resolve() {
450    let mut cmd =
451        Command::new("foo").subcommand(Command::new("bar").alias("baz").about("test subcmd"));
452    assert_eq!(
453        cmd.find_subcommand("baz")
454            .unwrap()
455            .get_about()
456            .unwrap()
457            .to_string(),
458        "test subcmd"
459    );
460
461    let true_name = cmd.find_subcommand("baz").unwrap().get_name().to_string();
462    assert_eq!(true_name, "bar");
463
464    cmd = cmd.mut_subcommand(&*true_name, |subcmd| subcmd.about("modified about"));
465    assert_eq!(
466        cmd.find_subcommand("baz")
467            .unwrap()
468            .get_about()
469            .unwrap()
470            .to_string(),
471        "modified about"
472    );
473}
474
475#[test]
476fn issue_3669_command_build_recurses() {
477    let mut cmd = Command::new("ctest").subcommand(
478        Command::new("subcmd").subcommand(
479            Command::new("multi")
480                .about("tests subcommands")
481                .author("Kevin K. <kbknapp@gmail.com>")
482                .version("0.1")
483                .arg(clap::arg!(
484                    <FLAG>                    "tests flags"
485                )),
486        ),
487    );
488    cmd.build();
489}
490