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::{Parser, Subcommand};
16
17 #[derive(Parser, PartialEq, Debug)]
18 struct Opt {
19 #[arg(short, long)]
20 force: bool,
21 #[arg(short, long, action = clap::ArgAction::Count)]
22 verbose: u8,
23 #[command(subcommand)]
24 cmd: Sub,
25 }
26
27 #[derive(Subcommand, PartialEq, Debug)]
28 enum Sub {
29 Fetch {},
30 Add {},
31 }
32
33 #[derive(Parser, PartialEq, Debug)]
34 struct Opt2 {
35 #[arg(short, long)]
36 force: bool,
37 #[arg(short, long, action = clap::ArgAction::Count)]
38 verbose: u8,
39 #[command(subcommand)]
40 cmd: Option<Sub>,
41 }
42
43 #[test]
test_no_cmd()44 fn test_no_cmd() {
45 let result = Opt::try_parse_from(["test"]);
46 assert!(result.is_err());
47
48 assert_eq!(
49 Opt2 {
50 force: false,
51 verbose: 0,
52 cmd: None
53 },
54 Opt2::try_parse_from(["test"]).unwrap()
55 );
56 }
57
58 #[test]
test_fetch()59 fn test_fetch() {
60 assert_eq!(
61 Opt {
62 force: false,
63 verbose: 3,
64 cmd: Sub::Fetch {}
65 },
66 Opt::try_parse_from(["test", "-vvv", "fetch"]).unwrap()
67 );
68 assert_eq!(
69 Opt {
70 force: true,
71 verbose: 0,
72 cmd: Sub::Fetch {}
73 },
74 Opt::try_parse_from(["test", "--force", "fetch"]).unwrap()
75 );
76 }
77
78 #[test]
test_add()79 fn test_add() {
80 assert_eq!(
81 Opt {
82 force: false,
83 verbose: 0,
84 cmd: Sub::Add {}
85 },
86 Opt::try_parse_from(["test", "add"]).unwrap()
87 );
88 assert_eq!(
89 Opt {
90 force: false,
91 verbose: 2,
92 cmd: Sub::Add {}
93 },
94 Opt::try_parse_from(["test", "-vv", "add"]).unwrap()
95 );
96 }
97
98 #[test]
test_badinput()99 fn test_badinput() {
100 let result = Opt::try_parse_from(["test", "badcmd"]);
101 assert!(result.is_err());
102 let result = Opt::try_parse_from(["test", "add", "--verbose"]);
103 assert!(result.is_err());
104 let result = Opt::try_parse_from(["test", "--badopt", "add"]);
105 assert!(result.is_err());
106 let result = Opt::try_parse_from(["test", "add", "--badopt"]);
107 assert!(result.is_err());
108 }
109
110 #[derive(Parser, PartialEq, Debug)]
111 struct Opt3 {
112 #[arg(short, long)]
113 all: bool,
114 #[command(subcommand)]
115 cmd: Sub2,
116 }
117
118 #[derive(Subcommand, PartialEq, Debug)]
119 enum Sub2 {
120 Foo {
121 file: String,
122 #[command(subcommand)]
123 cmd: Sub3,
124 },
125 Bar {},
126 }
127
128 #[derive(Subcommand, PartialEq, Debug)]
129 enum Sub3 {
130 Baz {},
131 Quux {},
132 }
133
134 #[test]
test_subsubcommand()135 fn test_subsubcommand() {
136 assert_eq!(
137 Opt3 {
138 all: true,
139 cmd: Sub2::Foo {
140 file: "lib.rs".to_string(),
141 cmd: Sub3::Quux {}
142 }
143 },
144 Opt3::try_parse_from(["test", "--all", "foo", "lib.rs", "quux"]).unwrap()
145 );
146 }
147
148 #[derive(Parser, PartialEq, Debug)]
149 enum SubSubCmdWithOption {
150 Remote {
151 #[command(subcommand)]
152 cmd: Option<Remote>,
153 },
154 Stash {
155 #[command(subcommand)]
156 cmd: Stash,
157 },
158 }
159 #[derive(Subcommand, PartialEq, Debug)]
160 enum Remote {
161 Add { name: String, url: String },
162 Remove { name: String },
163 }
164
165 #[derive(Subcommand, PartialEq, Debug)]
166 enum Stash {
167 Save,
168 Pop,
169 }
170
171 #[test]
sub_sub_cmd_with_option()172 fn sub_sub_cmd_with_option() {
173 fn make(args: &[&str]) -> Option<SubSubCmdWithOption> {
174 SubSubCmdWithOption::try_parse_from(args).ok()
175 }
176 assert_eq!(
177 Some(SubSubCmdWithOption::Remote { cmd: None }),
178 make(&["", "remote"])
179 );
180 assert_eq!(
181 Some(SubSubCmdWithOption::Remote {
182 cmd: Some(Remote::Add {
183 name: "origin".into(),
184 url: "http".into()
185 })
186 }),
187 make(&["", "remote", "add", "origin", "http"])
188 );
189 assert_eq!(
190 Some(SubSubCmdWithOption::Stash { cmd: Stash::Save }),
191 make(&["", "stash", "save"])
192 );
193 assert_eq!(None, make(&["", "stash"]));
194 }
195