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
15 use clap::builder::BoolishValueParser;
16 use clap::builder::TypedValueParser as _;
17 use clap::ArgAction;
18 use clap::CommandFactory;
19 use clap::Parser;
20
21 #[test]
bool_type_is_flag()22 fn 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]
non_bool_type_flag()48 fn 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
inferred_help()84 fn 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
inferred_version()105 fn 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]
count()128 fn 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]
mixed_type_flags()162 fn 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]
ignore_qualified_bool_type()216 fn 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]
override_implicit_action()245 fn 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]
override_implicit_from_flag_positional()264 fn 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]
unit_for_negation()283 fn 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