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 crate::utils;
16
17use clap::{Args, Parser, Subcommand};
18
19#[test]
20fn flatten() {
21    #[derive(Args, PartialEq, Debug)]
22    struct Common {
23        arg: i32,
24    }
25
26    #[derive(Parser, PartialEq, Debug)]
27    struct Opt {
28        #[command(flatten)]
29        common: Common,
30    }
31    assert_eq!(
32        Opt {
33            common: Common { arg: 42 }
34        },
35        Opt::try_parse_from(["test", "42"]).unwrap()
36    );
37    assert!(Opt::try_parse_from(["test"]).is_err());
38    assert!(Opt::try_parse_from(["test", "42", "24"]).is_err());
39}
40
41#[cfg(debug_assertions)]
42#[test]
43#[should_panic]
44fn flatten_twice() {
45    #[derive(Args, PartialEq, Debug)]
46    struct Common {
47        arg: i32,
48    }
49
50    #[derive(Parser, PartialEq, Debug)]
51    struct Opt {
52        #[command(flatten)]
53        c1: Common,
54        // Defines "arg" twice, so this should not work.
55        #[command(flatten)]
56        c2: Common,
57    }
58    Opt::try_parse_from(["test", "42", "43"]).unwrap();
59}
60
61#[test]
62fn flatten_in_subcommand() {
63    #[derive(Args, PartialEq, Debug)]
64    struct Common {
65        arg: i32,
66    }
67
68    #[derive(Args, PartialEq, Debug)]
69    struct Add {
70        #[arg(short)]
71        interactive: bool,
72        #[command(flatten)]
73        common: Common,
74    }
75
76    #[derive(Parser, PartialEq, Debug)]
77    enum Opt {
78        Fetch {
79            #[arg(short)]
80            all: bool,
81            #[command(flatten)]
82            common: Common,
83        },
84
85        Add(Add),
86    }
87
88    assert_eq!(
89        Opt::Fetch {
90            all: false,
91            common: Common { arg: 42 }
92        },
93        Opt::try_parse_from(["test", "fetch", "42"]).unwrap()
94    );
95    assert_eq!(
96        Opt::Add(Add {
97            interactive: true,
98            common: Common { arg: 43 }
99        }),
100        Opt::try_parse_from(["test", "add", "-i", "43"]).unwrap()
101    );
102}
103
104#[test]
105fn update_args_with_flatten() {
106    #[derive(Args, PartialEq, Debug)]
107    struct Common {
108        arg: i32,
109    }
110
111    #[derive(Parser, PartialEq, Debug)]
112    struct Opt {
113        #[command(flatten)]
114        common: Common,
115    }
116
117    let mut opt = Opt {
118        common: Common { arg: 42 },
119    };
120    opt.try_update_from(["test"]).unwrap();
121    assert_eq!(Opt::try_parse_from(["test", "42"]).unwrap(), opt);
122
123    let mut opt = Opt {
124        common: Common { arg: 42 },
125    };
126    opt.try_update_from(["test", "52"]).unwrap();
127    assert_eq!(Opt::try_parse_from(["test", "52"]).unwrap(), opt);
128}
129
130#[derive(Subcommand, PartialEq, Debug)]
131enum BaseCli {
132    Command1(Command1),
133}
134
135#[derive(Args, PartialEq, Debug)]
136struct Command1 {
137    arg1: i32,
138
139    arg2: i32,
140}
141
142#[derive(Args, PartialEq, Debug)]
143struct Command2 {
144    arg2: i32,
145}
146
147#[derive(Parser, PartialEq, Debug)]
148enum Opt {
149    #[command(flatten)]
150    BaseCli(BaseCli),
151    Command2(Command2),
152}
153
154#[test]
155fn merge_subcommands_with_flatten() {
156    assert_eq!(
157        Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42, arg2: 44 })),
158        Opt::try_parse_from(["test", "command1", "42", "44"]).unwrap()
159    );
160    assert_eq!(
161        Opt::Command2(Command2 { arg2: 43 }),
162        Opt::try_parse_from(["test", "command2", "43"]).unwrap()
163    );
164}
165
166#[test]
167fn update_subcommands_with_flatten() {
168    let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
169    opt.try_update_from(["test", "command1", "42", "44"])
170        .unwrap();
171    assert_eq!(
172        Opt::try_parse_from(["test", "command1", "42", "44"]).unwrap(),
173        opt
174    );
175
176    let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
177    opt.try_update_from(["test", "command1", "42"]).unwrap();
178    assert_eq!(
179        Opt::try_parse_from(["test", "command1", "42", "14"]).unwrap(),
180        opt
181    );
182
183    let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
184    opt.try_update_from(["test", "command2", "43"]).unwrap();
185    assert_eq!(
186        Opt::try_parse_from(["test", "command2", "43"]).unwrap(),
187        opt
188    );
189}
190
191#[test]
192fn flatten_with_doc_comment() {
193    #[derive(Args, PartialEq, Debug)]
194    struct Common {
195        /// This is an arg. Arg means "argument". Command line argument.
196        arg: i32,
197    }
198
199    #[derive(Parser, PartialEq, Debug)]
200    struct Opt {
201        /// The very important comment that clippy had me put here.
202        /// It knows better.
203        #[command(flatten)]
204        common: Common,
205    }
206    assert_eq!(
207        Opt {
208            common: Common { arg: 42 }
209        },
210        Opt::try_parse_from(["test", "42"]).unwrap()
211    );
212
213    let help = utils::get_help::<Opt>();
214    assert!(help.contains("This is an arg."));
215    assert!(!help.contains("The very important"));
216}
217
218#[test]
219fn docstrings_ordering_with_multiple_command() {
220    /// This is the docstring for Flattened
221    #[derive(Args)]
222    struct Flattened {
223        #[arg(long)]
224        foo: bool,
225    }
226
227    /// This is the docstring for Command
228    #[derive(Parser)]
229    struct Command {
230        #[command(flatten)]
231        flattened: Flattened,
232    }
233
234    let short_help = utils::get_help::<Command>();
235
236    assert!(short_help.contains("This is the docstring for Command"));
237}
238
239#[test]
240fn docstrings_ordering_with_multiple_clap_partial() {
241    /// This is the docstring for Flattened
242    #[derive(Args)]
243    struct Flattened {
244        #[arg(long)]
245        foo: bool,
246    }
247
248    #[derive(Parser)]
249    struct Command {
250        #[command(flatten)]
251        flattened: Flattened,
252    }
253
254    let short_help = utils::get_help::<Command>();
255
256    assert!(short_help.contains("This is the docstring for Flattened"));
257}
258
259#[test]
260fn optional_flatten() {
261    #[derive(Parser, Debug, PartialEq, Eq)]
262    struct Opt {
263        #[command(flatten)]
264        source: Option<Source>,
265    }
266
267    #[derive(clap::Args, Debug, PartialEq, Eq)]
268    struct Source {
269        crates: Vec<String>,
270        #[arg(long)]
271        path: Option<std::path::PathBuf>,
272        #[arg(long)]
273        git: Option<String>,
274    }
275
276    assert_eq!(Opt { source: None }, Opt::try_parse_from(["test"]).unwrap());
277    assert_eq!(
278        Opt {
279            source: Some(Source {
280                crates: vec!["serde".to_owned()],
281                path: None,
282                git: None,
283            }),
284        },
285        Opt::try_parse_from(["test", "serde"]).unwrap()
286    );
287    assert_eq!(
288        Opt {
289            source: Some(Source {
290                crates: Vec::new(),
291                path: Some("./".into()),
292                git: None,
293            }),
294        },
295        Opt::try_parse_from(["test", "--path=./"]).unwrap()
296    );
297}
298