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::error::ErrorKind;
16use clap::Parser;
17use std::num::ParseIntError;
18
19pub const DISPLAY_ORDER: usize = 2;
20
21// Check if the global settings compile
22#[derive(Parser, Debug, PartialEq, Eq)]
23#[command(group = clap::ArgGroup::new("foo"))]
24struct Opt {
25    #[arg(
26        long = "x",
27        display_order = DISPLAY_ORDER,
28        next_line_help = true,
29        default_value = "0",
30        require_equals = true,
31    )]
32    x: i32,
33
34    #[arg(short = 'l', long = "level", aliases = ["set-level", "lvl"])]
35    level: String,
36
37    #[arg(long("values"))]
38    values: Vec<i32>,
39
40    #[arg(id = "FILE", requires_if("FILE", "values"))]
41    files: Vec<String>,
42}
43
44#[test]
45fn test_slice() {
46    assert_eq!(
47        Opt {
48            x: 0,
49            level: "1".to_string(),
50            files: Vec::new(),
51            values: vec![],
52        },
53        Opt::try_parse_from(["test", "-l", "1"]).unwrap()
54    );
55    assert_eq!(
56        Opt {
57            x: 0,
58            level: "1".to_string(),
59            files: Vec::new(),
60            values: vec![],
61        },
62        Opt::try_parse_from(["test", "--level", "1"]).unwrap()
63    );
64    assert_eq!(
65        Opt {
66            x: 0,
67            level: "1".to_string(),
68            files: Vec::new(),
69            values: vec![],
70        },
71        Opt::try_parse_from(["test", "--set-level", "1"]).unwrap()
72    );
73    assert_eq!(
74        Opt {
75            x: 0,
76            level: "1".to_string(),
77            files: Vec::new(),
78            values: vec![],
79        },
80        Opt::try_parse_from(["test", "--lvl", "1"]).unwrap()
81    );
82}
83
84#[test]
85fn test_multi_args() {
86    assert_eq!(
87        Opt {
88            x: 0,
89            level: "1".to_string(),
90            files: vec!["file".to_string()],
91            values: vec![],
92        },
93        Opt::try_parse_from(["test", "-l", "1", "file"]).unwrap()
94    );
95    assert_eq!(
96        Opt {
97            x: 0,
98            level: "1".to_string(),
99            files: vec!["FILE".to_string()],
100            values: vec![1],
101        },
102        Opt::try_parse_from(["test", "-l", "1", "--values", "1", "--", "FILE"]).unwrap()
103    );
104}
105
106#[test]
107fn test_multi_args_fail() {
108    let result = Opt::try_parse_from(["test", "-l", "1", "--", "FILE"]);
109    assert!(result.is_err());
110}
111
112#[test]
113fn test_bool() {
114    assert_eq!(
115        Opt {
116            x: 1,
117            level: "1".to_string(),
118            files: vec![],
119            values: vec![],
120        },
121        Opt::try_parse_from(["test", "-l", "1", "--x=1"]).unwrap()
122    );
123    let result = Opt::try_parse_from(["test", "-l", "1", "--x", "1"]);
124    assert!(result.is_err());
125    assert_eq!(result.unwrap_err().kind(), ErrorKind::NoEquals);
126}
127
128fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
129    u64::from_str_radix(input, 16)
130}
131
132#[derive(Parser, PartialEq, Debug)]
133struct HexOpt {
134    #[arg(short, value_parser = parse_hex)]
135    number: u64,
136}
137
138#[test]
139#[cfg(feature = "error-context")]
140fn test_parse_hex_function_path() {
141    assert_eq!(
142        HexOpt { number: 5 },
143        HexOpt::try_parse_from(["test", "-n", "5"]).unwrap()
144    );
145    assert_eq!(
146        HexOpt {
147            number: 0x00ab_cdef
148        },
149        HexOpt::try_parse_from(["test", "-n", "abcdef"]).unwrap()
150    );
151
152    let err = HexOpt::try_parse_from(["test", "-n", "gg"]).unwrap_err();
153    assert!(
154        err.to_string().contains("invalid digit found in string"),
155        "{}",
156        err
157    );
158}
159