1use clap::{Arg, ArgAction, ArgMatches, Command};
2
3fn occurrences_as_vec_vec<'a>(m: &'a ArgMatches, name: &str) -> Vec<Vec<&'a String>> {
4    m.get_occurrences(name)
5        .unwrap()
6        .map(Iterator::collect)
7        .collect()
8}
9
10#[test]
11fn grouped_value_works() {
12    let m = Command::new("cli")
13        .arg(
14            Arg::new("option")
15                .long("option")
16                .action(ArgAction::Set)
17                .num_args(1..)
18                .action(ArgAction::Append),
19        )
20        .try_get_matches_from([
21            "cli",
22            "--option",
23            "fr_FR:mon option 1",
24            "en_US:my option 1",
25            "--option",
26            "fr_FR:mon option 2",
27            "en_US:my option 2",
28        ])
29        .unwrap();
30    let grouped_vals = occurrences_as_vec_vec(&m, "option");
31    assert_eq!(
32        grouped_vals,
33        vec![
34            vec!["fr_FR:mon option 1", "en_US:my option 1",],
35            vec!["fr_FR:mon option 2", "en_US:my option 2",],
36        ]
37    );
38}
39
40#[test]
41fn issue_1026() {
42    let m = Command::new("cli")
43        .arg(Arg::new("server").short('s').action(ArgAction::Set))
44        .arg(Arg::new("user").short('u').action(ArgAction::Set))
45        .arg(
46            Arg::new("target")
47                .long("target")
48                .action(ArgAction::Set)
49                .num_args(1..)
50                .action(ArgAction::Append),
51        )
52        .try_get_matches_from([
53            "backup", "-s", "server", "-u", "user", "--target", "target1", "file1", "file2",
54            "file3", "--target", "target2", "file4", "file5", "file6", "file7", "--target",
55            "target3", "file8",
56        ])
57        .unwrap();
58    let grouped_vals = occurrences_as_vec_vec(&m, "target");
59    assert_eq!(
60        grouped_vals,
61        vec![
62            vec!["target1", "file1", "file2", "file3"],
63            vec!["target2", "file4", "file5", "file6", "file7",],
64            vec!["target3", "file8"]
65        ]
66    );
67}
68
69#[test]
70fn grouped_value_long_flag_delimiter() {
71    let m = Command::new("myapp")
72        .arg(
73            Arg::new("option")
74                .long("option")
75                .action(ArgAction::Set)
76                .value_delimiter(',')
77                .num_args(1..)
78                .action(ArgAction::Append),
79        )
80        .try_get_matches_from(vec![
81            "myapp",
82            "--option=hmm",
83            "--option=val1,val2,val3",
84            "--option",
85            "alice,bob",
86        ])
87        .unwrap();
88    let grouped_vals = occurrences_as_vec_vec(&m, "option");
89    assert_eq!(
90        grouped_vals,
91        vec![
92            vec!["hmm"],
93            vec!["val1", "val2", "val3"],
94            vec!["alice", "bob"]
95        ]
96    );
97}
98
99#[test]
100fn grouped_value_short_flag_delimiter() {
101    let m = Command::new("myapp")
102        .arg(
103            Arg::new("option")
104                .short('o')
105                .action(ArgAction::Set)
106                .value_delimiter(',')
107                .num_args(1..)
108                .action(ArgAction::Append),
109        )
110        .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"])
111        .unwrap();
112    let grouped_vals = occurrences_as_vec_vec(&m, "option");
113    assert_eq!(
114        grouped_vals,
115        vec![vec!["foo"], vec!["val1", "val2", "val3"], vec!["bar"]]
116    );
117}
118
119#[test]
120fn grouped_value_positional_arg() {
121    let m = Command::new("multiple_values")
122        .arg(
123            Arg::new("pos")
124                .help("multiple positionals")
125                .action(ArgAction::Set)
126                .num_args(1..),
127        )
128        .try_get_matches_from(vec![
129            "myprog", "val1", "val2", "val3", "val4", "val5", "val6",
130        ])
131        .unwrap();
132    let grouped_vals = occurrences_as_vec_vec(&m, "pos");
133    assert_eq!(
134        grouped_vals,
135        vec![vec!["val1", "val2", "val3", "val4", "val5", "val6"]]
136    );
137}
138
139#[test]
140fn grouped_value_multiple_positional_arg() {
141    let m = Command::new("multiple_values")
142        .arg(Arg::new("pos1").help("multiple positionals"))
143        .arg(
144            Arg::new("pos2")
145                .help("multiple positionals")
146                .action(ArgAction::Set)
147                .num_args(1..),
148        )
149        .try_get_matches_from(vec![
150            "myprog", "val1", "val2", "val3", "val4", "val5", "val6",
151        ])
152        .unwrap();
153    let grouped_vals = occurrences_as_vec_vec(&m, "pos2");
154    assert_eq!(
155        grouped_vals,
156        vec![vec!["val2", "val3", "val4", "val5", "val6"]]
157    );
158}
159
160#[test]
161fn grouped_value_multiple_positional_arg_last_multiple() {
162    let m = Command::new("multiple_values")
163        .arg(Arg::new("pos1").help("multiple positionals"))
164        .arg(
165            Arg::new("pos2")
166                .help("multiple positionals")
167                .action(ArgAction::Set)
168                .num_args(1..)
169                .last(true),
170        )
171        .try_get_matches_from(vec![
172            "myprog", "val1", "--", "val2", "val3", "val4", "val5", "val6",
173        ])
174        .unwrap();
175    let grouped_vals = occurrences_as_vec_vec(&m, "pos2");
176    assert_eq!(
177        grouped_vals,
178        vec![vec!["val2", "val3", "val4", "val5", "val6"]]
179    );
180}
181
182#[test]
183fn grouped_interleaved_positional_values() {
184    let cmd = clap::Command::new("foo")
185        .arg(clap::Arg::new("pos").num_args(1..))
186        .arg(
187            clap::Arg::new("flag")
188                .short('f')
189                .long("flag")
190                .action(ArgAction::Set)
191                .action(ArgAction::Append),
192        );
193
194    let m = cmd
195        .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"])
196        .unwrap();
197
198    let pos = occurrences_as_vec_vec(&m, "pos");
199    assert_eq!(pos, vec![vec!["1", "2"], vec!["3"], vec!["4"]]);
200
201    let flag = occurrences_as_vec_vec(&m, "flag");
202    assert_eq!(flag, vec![vec!["a"], vec!["b"]]);
203}
204
205#[test]
206fn grouped_interleaved_positional_occurrences() {
207    let cmd = clap::Command::new("foo")
208        .arg(clap::Arg::new("pos").num_args(1..))
209        .arg(
210            clap::Arg::new("flag")
211                .short('f')
212                .long("flag")
213                .action(ArgAction::Set)
214                .action(ArgAction::Append),
215        );
216
217    let m = cmd
218        .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"])
219        .unwrap();
220
221    let pos = occurrences_as_vec_vec(&m, "pos");
222    assert_eq!(pos, vec![vec!["1", "2"], vec!["3"], vec!["4"]]);
223
224    let flag = occurrences_as_vec_vec(&m, "flag");
225    assert_eq!(flag, vec![vec!["a"], vec!["b"]]);
226}
227
228#[test]
229fn issue_2171() {
230    let schema = Command::new("ripgrep#1701 reproducer")
231        .args_override_self(true)
232        .arg(
233            Arg::new("pretty")
234                .short('p')
235                .long("pretty")
236                .action(ArgAction::SetTrue),
237        )
238        .arg(
239            Arg::new("search_zip")
240                .short('z')
241                .long("search-zip")
242                .action(ArgAction::SetTrue),
243        );
244
245    let test_args = [
246        vec!["reproducer", "-pz", "-p"],
247        vec!["reproducer", "-pzp"],
248        vec!["reproducer", "-zpp"],
249        vec!["reproducer", "-pp", "-z"],
250        vec!["reproducer", "-p", "-p", "-z"],
251        vec!["reproducer", "-p", "-pz"],
252        vec!["reproducer", "-ppz"],
253    ];
254
255    for argv in test_args {
256        let _ = schema.clone().try_get_matches_from(argv).unwrap();
257    }
258}
259