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.
10use clap::Parser;
11
12#[test]
13fn basic() {
14    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
15    enum ArgChoice {
16        Foo,
17        Bar,
18    }
19
20    #[derive(Parser, PartialEq, Debug)]
21    struct Opt {
22        #[arg(value_enum)]
23        arg: ArgChoice,
24    }
25
26    assert_eq!(
27        Opt {
28            arg: ArgChoice::Foo
29        },
30        Opt::try_parse_from(["", "foo"]).unwrap()
31    );
32    assert_eq!(
33        Opt {
34            arg: ArgChoice::Bar
35        },
36        Opt::try_parse_from(["", "bar"]).unwrap()
37    );
38    assert!(Opt::try_parse_from(["", "fOo"]).is_err());
39}
40
41#[test]
42fn default_value() {
43    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
44    enum ArgChoice {
45        Foo,
46        Bar,
47    }
48
49    impl Default for ArgChoice {
50        fn default() -> Self {
51            Self::Bar
52        }
53    }
54
55    #[derive(Parser, PartialEq, Debug)]
56    struct Opt {
57        #[arg(value_enum, default_value_t)]
58        arg: ArgChoice,
59    }
60
61    assert_eq!(
62        Opt {
63            arg: ArgChoice::Foo
64        },
65        Opt::try_parse_from(["", "foo"]).unwrap()
66    );
67    assert_eq!(
68        Opt {
69            arg: ArgChoice::Bar
70        },
71        Opt::try_parse_from(["", "bar"]).unwrap()
72    );
73    assert_eq!(
74        Opt {
75            arg: ArgChoice::Bar
76        },
77        Opt::try_parse_from([""]).unwrap()
78    );
79}
80
81#[test]
82fn vec_for_default_values_t() {
83    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
84    enum ArgChoice {
85        Foo,
86        Bar,
87    }
88
89    #[derive(Parser, PartialEq, Debug)]
90    struct Opt {
91        #[arg(value_enum, default_values_t = vec![ArgChoice::Foo, ArgChoice::Bar])]
92        arg1: Vec<ArgChoice>,
93
94        #[arg(
95            long,
96            value_enum,
97            default_values_t = clap::ValueEnum::value_variants()
98        )]
99        arg2: Vec<ArgChoice>,
100    }
101
102    assert_eq!(
103        Opt {
104            arg1: vec![ArgChoice::Foo],
105            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
106        },
107        Opt::try_parse_from(["", "foo"]).unwrap()
108    );
109    assert_eq!(
110        Opt {
111            arg1: vec![ArgChoice::Bar],
112            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
113        },
114        Opt::try_parse_from(["", "bar"]).unwrap()
115    );
116    assert_eq!(
117        Opt {
118            arg1: vec![ArgChoice::Foo, ArgChoice::Bar],
119            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
120        },
121        Opt::try_parse_from([""]).unwrap()
122    );
123    assert_eq!(
124        Opt {
125            arg1: vec![ArgChoice::Foo, ArgChoice::Bar],
126            arg2: vec![ArgChoice::Foo]
127        },
128        Opt::try_parse_from(["", "--arg2", "foo"]).unwrap()
129    );
130}
131
132#[test]
133fn vec_for_default_values_os_t() {
134    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
135    enum ArgChoice {
136        Foo,
137        Bar,
138    }
139
140    #[derive(Parser, PartialEq, Debug)]
141    struct Opt {
142        #[arg(value_enum, default_values_os_t = vec![ArgChoice::Foo, ArgChoice::Bar])]
143        arg: Vec<ArgChoice>,
144
145        #[arg(
146            long,
147            value_enum,
148            default_values_os_t = clap::ValueEnum::value_variants()
149        )]
150        arg2: Vec<ArgChoice>,
151    }
152
153    assert_eq!(
154        Opt {
155            arg: vec![ArgChoice::Foo],
156            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
157        },
158        Opt::try_parse_from(["", "foo"]).unwrap()
159    );
160    assert_eq!(
161        Opt {
162            arg: vec![ArgChoice::Bar],
163            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
164        },
165        Opt::try_parse_from(["", "bar"]).unwrap()
166    );
167    assert_eq!(
168        Opt {
169            arg: vec![ArgChoice::Foo, ArgChoice::Bar],
170            arg2: vec![ArgChoice::Foo, ArgChoice::Bar]
171        },
172        Opt::try_parse_from([""]).unwrap()
173    );
174    assert_eq!(
175        Opt {
176            arg: vec![ArgChoice::Foo, ArgChoice::Bar],
177            arg2: vec![ArgChoice::Foo]
178        },
179        Opt::try_parse_from(["", "--arg2", "foo"]).unwrap()
180    );
181}
182
183#[test]
184fn multi_word_is_renamed_kebab() {
185    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
186    #[allow(non_camel_case_types)]
187    enum ArgChoice {
188        FooBar,
189        BAR_BAZ,
190    }
191
192    #[derive(Parser, PartialEq, Debug)]
193    struct Opt {
194        #[arg(value_enum)]
195        arg: ArgChoice,
196    }
197
198    assert_eq!(
199        Opt {
200            arg: ArgChoice::FooBar
201        },
202        Opt::try_parse_from(["", "foo-bar"]).unwrap()
203    );
204    assert_eq!(
205        Opt {
206            arg: ArgChoice::BAR_BAZ
207        },
208        Opt::try_parse_from(["", "bar-baz"]).unwrap()
209    );
210    assert!(Opt::try_parse_from(["", "FooBar"]).is_err());
211}
212
213#[test]
214fn variant_with_defined_casing() {
215    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
216    enum ArgChoice {
217        #[value(rename_all = "screaming_snake")]
218        FooBar,
219    }
220
221    #[derive(Parser, PartialEq, Debug)]
222    struct Opt {
223        #[arg(value_enum)]
224        arg: ArgChoice,
225    }
226
227    assert_eq!(
228        Opt {
229            arg: ArgChoice::FooBar
230        },
231        Opt::try_parse_from(["", "FOO_BAR"]).unwrap()
232    );
233    assert!(Opt::try_parse_from(["", "FooBar"]).is_err());
234}
235
236#[test]
237fn casing_is_propagated_from_parent() {
238    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
239    #[value(rename_all = "screaming_snake")]
240    enum ArgChoice {
241        FooBar,
242    }
243
244    #[derive(Parser, PartialEq, Debug)]
245    struct Opt {
246        #[arg(value_enum)]
247        arg: ArgChoice,
248    }
249
250    assert_eq!(
251        Opt {
252            arg: ArgChoice::FooBar
253        },
254        Opt::try_parse_from(["", "FOO_BAR"]).unwrap()
255    );
256    assert!(Opt::try_parse_from(["", "FooBar"]).is_err());
257}
258
259#[test]
260fn casing_propagation_is_overridden() {
261    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
262    #[value(rename_all = "screaming_snake")]
263    enum ArgChoice {
264        #[value(rename_all = "camel")]
265        FooBar,
266    }
267
268    #[derive(Parser, PartialEq, Debug)]
269    struct Opt {
270        #[arg(value_enum)]
271        arg: ArgChoice,
272    }
273
274    assert_eq!(
275        Opt {
276            arg: ArgChoice::FooBar
277        },
278        Opt::try_parse_from(["", "fooBar"]).unwrap()
279    );
280    assert!(Opt::try_parse_from(["", "FooBar"]).is_err());
281    assert!(Opt::try_parse_from(["", "FOO_BAR"]).is_err());
282}
283
284#[test]
285fn ignore_case() {
286    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
287    enum ArgChoice {
288        Foo,
289    }
290
291    #[derive(Parser, PartialEq, Debug)]
292    struct Opt {
293        #[arg(value_enum, ignore_case(true))]
294        arg: ArgChoice,
295    }
296
297    assert_eq!(
298        Opt {
299            arg: ArgChoice::Foo
300        },
301        Opt::try_parse_from(["", "foo"]).unwrap()
302    );
303    assert_eq!(
304        Opt {
305            arg: ArgChoice::Foo
306        },
307        Opt::try_parse_from(["", "fOo"]).unwrap()
308    );
309}
310
311#[test]
312fn ignore_case_set_to_false() {
313    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
314    enum ArgChoice {
315        Foo,
316    }
317
318    #[derive(Parser, PartialEq, Debug)]
319    struct Opt {
320        #[arg(value_enum, ignore_case(false))]
321        arg: ArgChoice,
322    }
323
324    assert_eq!(
325        Opt {
326            arg: ArgChoice::Foo
327        },
328        Opt::try_parse_from(["", "foo"]).unwrap()
329    );
330    assert!(Opt::try_parse_from(["", "fOo"]).is_err());
331}
332
333#[test]
334fn alias() {
335    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
336    enum ArgChoice {
337        #[value(alias = "TOTP")]
338        Totp,
339    }
340
341    #[derive(Parser, PartialEq, Debug)]
342    struct Opt {
343        #[arg(value_enum, ignore_case(false))]
344        arg: ArgChoice,
345    }
346
347    assert_eq!(
348        Opt {
349            arg: ArgChoice::Totp
350        },
351        Opt::try_parse_from(["", "totp"]).unwrap()
352    );
353    assert_eq!(
354        Opt {
355            arg: ArgChoice::Totp
356        },
357        Opt::try_parse_from(["", "TOTP"]).unwrap()
358    );
359}
360
361#[test]
362fn multiple_alias() {
363    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
364    enum ArgChoice {
365        #[value(alias = "TOTP", alias = "t")]
366        Totp,
367    }
368
369    #[derive(Parser, PartialEq, Debug)]
370    struct Opt {
371        #[arg(value_enum, ignore_case(false))]
372        arg: ArgChoice,
373    }
374
375    assert_eq!(
376        Opt {
377            arg: ArgChoice::Totp
378        },
379        Opt::try_parse_from(["", "totp"]).unwrap()
380    );
381    assert_eq!(
382        Opt {
383            arg: ArgChoice::Totp
384        },
385        Opt::try_parse_from(["", "TOTP"]).unwrap()
386    );
387    assert_eq!(
388        Opt {
389            arg: ArgChoice::Totp
390        },
391        Opt::try_parse_from(["", "t"]).unwrap()
392    );
393}
394
395#[test]
396fn skip_variant() {
397    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
398    #[allow(dead_code)] // silence warning about `Baz` being unused
399    enum ArgChoice {
400        Foo,
401        Bar,
402        #[value(skip)]
403        Baz,
404    }
405
406    assert_eq!(
407        <ArgChoice as clap::ValueEnum>::value_variants()
408            .iter()
409            .map(clap::ValueEnum::to_possible_value)
410            .map(Option::unwrap)
411            .collect::<Vec<_>>(),
412        vec![
413            clap::builder::PossibleValue::new("foo"),
414            clap::builder::PossibleValue::new("bar")
415        ]
416    );
417
418    {
419        use clap::ValueEnum;
420        assert!(ArgChoice::from_str("foo", true).is_ok());
421        assert!(ArgChoice::from_str("bar", true).is_ok());
422        assert!(ArgChoice::from_str("baz", true).is_err());
423    }
424}
425
426#[test]
427fn skip_non_unit_variant() {
428    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
429    #[allow(dead_code)] // silence warning about `Baz` being unused
430    enum ArgChoice {
431        Foo,
432        Bar,
433        #[value(skip)]
434        Baz(usize),
435    }
436
437    assert_eq!(
438        <ArgChoice as clap::ValueEnum>::value_variants()
439            .iter()
440            .map(clap::ValueEnum::to_possible_value)
441            .map(Option::unwrap)
442            .collect::<Vec<_>>(),
443        vec![
444            clap::builder::PossibleValue::new("foo"),
445            clap::builder::PossibleValue::new("bar")
446        ]
447    );
448
449    {
450        use clap::ValueEnum;
451        assert!(ArgChoice::from_str("foo", true).is_ok());
452        assert!(ArgChoice::from_str("bar", true).is_ok());
453        assert!(ArgChoice::from_str("baz", true).is_err());
454    }
455}
456
457#[test]
458fn from_str_invalid() {
459    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
460    enum ArgChoice {
461        Foo,
462    }
463
464    {
465        use clap::ValueEnum;
466        assert!(ArgChoice::from_str("bar", true).is_err());
467    }
468}
469
470#[test]
471fn option_type() {
472    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
473    enum ArgChoice {
474        Foo,
475        Bar,
476    }
477
478    #[derive(Parser, PartialEq, Debug)]
479    struct Opt {
480        #[arg(value_enum)]
481        arg: Option<ArgChoice>,
482    }
483
484    assert_eq!(Opt { arg: None }, Opt::try_parse_from([""]).unwrap());
485    assert_eq!(
486        Opt {
487            arg: Some(ArgChoice::Foo)
488        },
489        Opt::try_parse_from(["", "foo"]).unwrap()
490    );
491    assert_eq!(
492        Opt {
493            arg: Some(ArgChoice::Bar)
494        },
495        Opt::try_parse_from(["", "bar"]).unwrap()
496    );
497    assert!(Opt::try_parse_from(["", "fOo"]).is_err());
498}
499
500#[test]
501fn option_option_type() {
502    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
503    enum ArgChoice {
504        Foo,
505        Bar,
506    }
507
508    #[derive(Parser, PartialEq, Debug)]
509    struct Opt {
510        #[arg(value_enum, long)]
511        arg: Option<Option<ArgChoice>>,
512    }
513
514    assert_eq!(Opt { arg: None }, Opt::try_parse_from([""]).unwrap());
515    assert_eq!(
516        Opt { arg: Some(None) },
517        Opt::try_parse_from(["", "--arg"]).unwrap()
518    );
519    assert_eq!(
520        Opt {
521            arg: Some(Some(ArgChoice::Foo))
522        },
523        Opt::try_parse_from(["", "--arg", "foo"]).unwrap()
524    );
525    assert_eq!(
526        Opt {
527            arg: Some(Some(ArgChoice::Bar))
528        },
529        Opt::try_parse_from(["", "--arg", "bar"]).unwrap()
530    );
531    assert!(Opt::try_parse_from(["", "--arg", "fOo"]).is_err());
532}
533
534#[test]
535fn vec_type() {
536    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
537    enum ArgChoice {
538        Foo,
539        Bar,
540    }
541
542    #[derive(Parser, PartialEq, Debug)]
543    struct Opt {
544        #[arg(value_enum, short, long)]
545        arg: Vec<ArgChoice>,
546    }
547
548    assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from([""]).unwrap());
549    assert_eq!(
550        Opt {
551            arg: vec![ArgChoice::Foo]
552        },
553        Opt::try_parse_from(["", "-a", "foo"]).unwrap()
554    );
555    assert_eq!(
556        Opt {
557            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
558        },
559        Opt::try_parse_from(["", "-a", "foo", "-a", "bar"]).unwrap()
560    );
561    assert!(Opt::try_parse_from(["", "-a", "fOo"]).is_err());
562}
563
564#[test]
565fn option_vec_type() {
566    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
567    enum ArgChoice {
568        Foo,
569        Bar,
570    }
571
572    #[derive(Parser, PartialEq, Debug)]
573    struct Opt {
574        #[arg(value_enum, short, long)]
575        arg: Option<Vec<ArgChoice>>,
576    }
577
578    assert_eq!(Opt { arg: None }, Opt::try_parse_from([""]).unwrap());
579    assert_eq!(
580        Opt {
581            arg: Some(vec![ArgChoice::Foo])
582        },
583        Opt::try_parse_from(["", "-a", "foo"]).unwrap()
584    );
585    assert_eq!(
586        Opt {
587            arg: Some(vec![ArgChoice::Foo, ArgChoice::Bar])
588        },
589        Opt::try_parse_from(["", "-a", "foo", "-a", "bar"]).unwrap()
590    );
591    assert!(Opt::try_parse_from(["", "-a", "fOo"]).is_err());
592}
593
594#[test]
595fn vec_type_default_value() {
596    #[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
597    enum ArgChoice {
598        Foo,
599        Bar,
600        Baz,
601    }
602
603    #[derive(Parser, PartialEq, Debug)]
604    struct Opt {
605        #[arg(
606            value_enum,
607            short,
608            long,
609            default_value = "foo,bar",
610            value_delimiter = ','
611        )]
612        arg: Vec<ArgChoice>,
613    }
614
615    assert_eq!(
616        Opt {
617            arg: vec![ArgChoice::Foo, ArgChoice::Bar]
618        },
619        Opt::try_parse_from([""]).unwrap()
620    );
621
622    assert_eq!(
623        Opt {
624            arg: vec![ArgChoice::Foo, ArgChoice::Baz]
625        },
626        Opt::try_parse_from(["", "-a", "foo,baz"]).unwrap()
627    );
628}
629