1// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3// Ana Hobden (@hoverbear) <operator@hoverbear.org>
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10//
11// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13// MIT/Apache 2.0 license.
14
15use clap::builder::BoolishValueParser;
16use clap::builder::TypedValueParser as _;
17use clap::ArgAction;
18use clap::CommandFactory;
19use clap::Parser;
20
21#[test]
22fn bool_type_is_flag() {
23    #[derive(Parser, PartialEq, Eq, Debug)]
24    #[command(args_override_self = true)]
25    struct Opt {
26        #[arg(short, long)]
27        alice: bool,
28    }
29
30    assert_eq!(Opt { alice: false }, Opt::try_parse_from(["test"]).unwrap());
31    assert_eq!(
32        Opt { alice: true },
33        Opt::try_parse_from(["test", "-a"]).unwrap()
34    );
35    assert_eq!(
36        Opt { alice: true },
37        Opt::try_parse_from(["test", "-a", "-a"]).unwrap()
38    );
39    assert_eq!(
40        Opt { alice: true },
41        Opt::try_parse_from(["test", "--alice"]).unwrap()
42    );
43    assert!(Opt::try_parse_from(["test", "-i"]).is_err());
44    assert!(Opt::try_parse_from(["test", "-a", "foo"]).is_err());
45}
46
47#[test]
48fn non_bool_type_flag() {
49    fn parse_from_flag(b: bool) -> usize {
50        if b {
51            10
52        } else {
53            5
54        }
55    }
56
57    #[derive(Parser, Debug)]
58    struct Opt {
59        #[arg(short, long, action = ArgAction::SetTrue, value_parser = BoolishValueParser::new().map(parse_from_flag))]
60        alice: usize,
61        #[arg(short, long, action = ArgAction::SetTrue, value_parser = BoolishValueParser::new().map(parse_from_flag))]
62        bob: usize,
63    }
64
65    let opt = Opt::try_parse_from(["test"]).unwrap();
66    assert_eq!(opt.alice, 5);
67    assert_eq!(opt.bob, 5);
68
69    let opt = Opt::try_parse_from(["test", "-a"]).unwrap();
70    assert_eq!(opt.alice, 10);
71    assert_eq!(opt.bob, 5);
72
73    let opt = Opt::try_parse_from(["test", "-b"]).unwrap();
74    assert_eq!(opt.alice, 5);
75    assert_eq!(opt.bob, 10);
76
77    let opt = Opt::try_parse_from(["test", "-b", "-a"]).unwrap();
78    assert_eq!(opt.alice, 10);
79    assert_eq!(opt.bob, 10);
80}
81
82#[test]
83#[ignore] // Not a good path for supporting this atm
84fn inferred_help() {
85    #[derive(Parser, PartialEq, Eq, Debug)]
86    struct Opt {
87        /// Foo
88        #[arg(short, long)]
89        help: bool,
90    }
91
92    let mut cmd = Opt::command();
93    cmd.build();
94    let arg = cmd.get_arguments().find(|a| a.get_id() == "help").unwrap();
95    assert_eq!(
96        arg.get_help().map(|s| s.to_string()),
97        Some("Foo".to_owned()),
98        "Incorrect help"
99    );
100    assert!(matches!(arg.get_action(), clap::ArgAction::Help));
101}
102
103#[test]
104#[ignore] // Not a good path for supporting this atm
105fn inferred_version() {
106    #[derive(Parser, PartialEq, Eq, Debug)]
107    struct Opt {
108        /// Foo
109        #[arg(short, long)]
110        version: bool,
111    }
112
113    let mut cmd = Opt::command();
114    cmd.build();
115    let arg = cmd
116        .get_arguments()
117        .find(|a| a.get_id() == "version")
118        .unwrap();
119    assert_eq!(
120        arg.get_help().map(|s| s.to_string()),
121        Some("Foo".to_owned()),
122        "Incorrect help"
123    );
124    assert!(matches!(arg.get_action(), clap::ArgAction::Version));
125}
126
127#[test]
128fn count() {
129    #[derive(Parser, PartialEq, Eq, Debug)]
130    struct Opt {
131        #[arg(short, long, action = clap::ArgAction::Count)]
132        alice: u8,
133        #[arg(short, long, action = clap::ArgAction::Count)]
134        bob: u8,
135    }
136
137    assert_eq!(
138        Opt { alice: 0, bob: 0 },
139        Opt::try_parse_from(["test"]).unwrap()
140    );
141    assert_eq!(
142        Opt { alice: 1, bob: 0 },
143        Opt::try_parse_from(["test", "-a"]).unwrap()
144    );
145    assert_eq!(
146        Opt { alice: 2, bob: 0 },
147        Opt::try_parse_from(["test", "-a", "-a"]).unwrap()
148    );
149    assert_eq!(
150        Opt { alice: 2, bob: 2 },
151        Opt::try_parse_from(["test", "-a", "--alice", "-bb"]).unwrap()
152    );
153    assert_eq!(
154        Opt { alice: 3, bob: 1 },
155        Opt::try_parse_from(["test", "-aaa", "--bob"]).unwrap()
156    );
157    assert!(Opt::try_parse_from(["test", "-i"]).is_err());
158    assert!(Opt::try_parse_from(["test", "-a", "foo"]).is_err());
159}
160
161#[test]
162fn mixed_type_flags() {
163    #[derive(Parser, PartialEq, Eq, Debug)]
164    struct Opt {
165        #[arg(short, long)]
166        alice: bool,
167        #[arg(short, long, action = clap::ArgAction::Count)]
168        bob: u8,
169    }
170
171    assert_eq!(
172        Opt {
173            alice: false,
174            bob: 0
175        },
176        Opt::try_parse_from(["test"]).unwrap()
177    );
178    assert_eq!(
179        Opt {
180            alice: true,
181            bob: 0
182        },
183        Opt::try_parse_from(["test", "-a"]).unwrap()
184    );
185    assert_eq!(
186        Opt {
187            alice: true,
188            bob: 0
189        },
190        Opt::try_parse_from(["test", "-a"]).unwrap()
191    );
192    assert_eq!(
193        Opt {
194            alice: false,
195            bob: 1
196        },
197        Opt::try_parse_from(["test", "-b"]).unwrap()
198    );
199    assert_eq!(
200        Opt {
201            alice: true,
202            bob: 1
203        },
204        Opt::try_parse_from(["test", "--alice", "--bob"]).unwrap()
205    );
206    assert_eq!(
207        Opt {
208            alice: true,
209            bob: 4
210        },
211        Opt::try_parse_from(["test", "-bb", "-a", "-bb"]).unwrap()
212    );
213}
214
215#[test]
216fn ignore_qualified_bool_type() {
217    mod inner {
218        #[allow(non_camel_case_types)]
219        #[derive(PartialEq, Eq, Debug, Clone)]
220        pub struct bool(pub String);
221
222        impl std::str::FromStr for self::bool {
223            type Err = String;
224
225            fn from_str(s: &str) -> Result<Self, Self::Err> {
226                Ok(self::bool(s.into()))
227            }
228        }
229    }
230
231    #[derive(Parser, PartialEq, Eq, Debug)]
232    struct Opt {
233        arg: inner::bool,
234    }
235
236    assert_eq!(
237        Opt {
238            arg: inner::bool("success".into())
239        },
240        Opt::try_parse_from(["test", "success"]).unwrap()
241    );
242}
243
244#[test]
245fn override_implicit_action() {
246    #[derive(Parser, PartialEq, Eq, Debug)]
247    struct Opt {
248        #[arg(long, action = clap::ArgAction::Set)]
249        arg: bool,
250    }
251
252    assert_eq!(
253        Opt { arg: false },
254        Opt::try_parse_from(["test", "--arg", "false"]).unwrap()
255    );
256
257    assert_eq!(
258        Opt { arg: true },
259        Opt::try_parse_from(["test", "--arg", "true"]).unwrap()
260    );
261}
262
263#[test]
264fn override_implicit_from_flag_positional() {
265    #[derive(Parser, PartialEq, Eq, Debug)]
266    struct Opt {
267        #[arg(action = clap::ArgAction::Set)]
268        arg: bool,
269    }
270
271    assert_eq!(
272        Opt { arg: false },
273        Opt::try_parse_from(["test", "false"]).unwrap()
274    );
275
276    assert_eq!(
277        Opt { arg: true },
278        Opt::try_parse_from(["test", "true"]).unwrap()
279    );
280}
281
282#[test]
283fn unit_for_negation() {
284    #[derive(Parser, PartialEq, Eq, Debug)]
285    struct Opt {
286        #[arg(long)]
287        arg: bool,
288        #[arg(long, action = ArgAction::SetTrue, overrides_with = "arg")]
289        no_arg: (),
290    }
291
292    assert_eq!(
293        Opt {
294            arg: false,
295            no_arg: ()
296        },
297        Opt::try_parse_from(["test"]).unwrap()
298    );
299
300    assert_eq!(
301        Opt {
302            arg: true,
303            no_arg: ()
304        },
305        Opt::try_parse_from(["test", "--arg"]).unwrap()
306    );
307
308    assert_eq!(
309        Opt {
310            arg: false,
311            no_arg: ()
312        },
313        Opt::try_parse_from(["test", "--no-arg"]).unwrap()
314    );
315
316    assert_eq!(
317        Opt {
318            arg: true,
319            no_arg: ()
320        },
321        Opt::try_parse_from(["test", "--no-arg", "--arg"]).unwrap()
322    );
323
324    assert_eq!(
325        Opt {
326            arg: false,
327            no_arg: ()
328        },
329        Opt::try_parse_from(["test", "--arg", "--no-arg"]).unwrap()
330    );
331}
332