1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 #[macro_use]
10 extern crate structopt;
11
12 use structopt::StructOpt;
13
14 use std::ffi::{OsStr, OsString};
15 use std::num::ParseIntError;
16 use std::path::PathBuf;
17
18 #[derive(StructOpt, PartialEq, Debug)]
19 struct PathOpt {
20 #[structopt(short = "p", long = "path", parse(from_os_str))]
21 path: PathBuf,
22
23 #[structopt(short = "d", default_value = "../", parse(from_os_str))]
24 default_path: PathBuf,
25
26 #[structopt(short = "v", parse(from_os_str))]
27 vector_path: Vec<PathBuf>,
28
29 #[structopt(short = "o", parse(from_os_str))]
30 option_path_1: Option<PathBuf>,
31
32 #[structopt(short = "q", parse(from_os_str))]
33 option_path_2: Option<PathBuf>,
34 }
35
36 #[test]
test_path_opt_simple()37 fn test_path_opt_simple() {
38 assert_eq!(
39 PathOpt {
40 path: PathBuf::from("/usr/bin"),
41 default_path: PathBuf::from("../"),
42 vector_path: vec![
43 PathBuf::from("/a/b/c"),
44 PathBuf::from("/d/e/f"),
45 PathBuf::from("/g/h/i"),
46 ],
47 option_path_1: None,
48 option_path_2: Some(PathBuf::from("j.zip")),
49 },
50 PathOpt::from_clap(&PathOpt::clap().get_matches_from(&[
51 "test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q",
52 "j.zip",
53 ]))
54 );
55 }
56
parse_hex(input: &str) -> Result<u64, ParseIntError>57 fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
58 u64::from_str_radix(input, 16)
59 }
60
61 #[derive(StructOpt, PartialEq, Debug)]
62 struct HexOpt {
63 #[structopt(short = "n", parse(try_from_str = "parse_hex"))]
64 number: u64,
65 }
66
67 #[test]
test_parse_hex()68 fn test_parse_hex() {
69 assert_eq!(
70 HexOpt { number: 5 },
71 HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"]))
72 );
73 assert_eq!(
74 HexOpt { number: 0xabcdef },
75 HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"]))
76 );
77
78 let err = HexOpt::clap()
79 .get_matches_from_safe(&["test", "-n", "gg"])
80 .unwrap_err();
81 assert!(err.message.contains("invalid digit found in string"), err);
82 }
83
custom_parser_1(_: &str) -> &'static str84 fn custom_parser_1(_: &str) -> &'static str {
85 "A"
86 }
custom_parser_2(_: &str) -> Result<&'static str, u32>87 fn custom_parser_2(_: &str) -> Result<&'static str, u32> {
88 Ok("B")
89 }
custom_parser_3(_: &OsStr) -> &'static str90 fn custom_parser_3(_: &OsStr) -> &'static str {
91 "C"
92 }
custom_parser_4(_: &OsStr) -> Result<&'static str, OsString>93 fn custom_parser_4(_: &OsStr) -> Result<&'static str, OsString> {
94 Ok("D")
95 }
96
97 #[derive(StructOpt, PartialEq, Debug)]
98 struct NoOpOpt {
99 #[structopt(short = "a", parse(from_str = "custom_parser_1"))]
100 a: &'static str,
101 #[structopt(short = "b", parse(try_from_str = "custom_parser_2"))]
102 b: &'static str,
103 #[structopt(short = "c", parse(from_os_str = "custom_parser_3"))]
104 c: &'static str,
105 #[structopt(short = "d", parse(try_from_os_str = "custom_parser_4"))]
106 d: &'static str,
107 }
108
109 #[test]
test_every_custom_parser()110 fn test_every_custom_parser() {
111 assert_eq!(
112 NoOpOpt {
113 a: "A",
114 b: "B",
115 c: "C",
116 d: "D"
117 },
118 NoOpOpt::from_clap(
119 &NoOpOpt::clap().get_matches_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"])
120 )
121 );
122 }
123
124 // Note: can't use `Vec<u8>` directly, as structopt would instead look for
125 // conversion function from `&str` to `u8`.
126 type Bytes = Vec<u8>;
127
128 #[derive(StructOpt, PartialEq, Debug)]
129 struct DefaultedOpt {
130 #[structopt(short = "b", parse(from_str))]
131 bytes: Bytes,
132
133 #[structopt(short = "i", parse(try_from_str))]
134 integer: u64,
135
136 #[structopt(short = "p", parse(from_os_str))]
137 path: PathBuf,
138 }
139
140 #[test]
test_parser_with_default_value()141 fn test_parser_with_default_value() {
142 assert_eq!(
143 DefaultedOpt {
144 bytes: b"E\xc2\xb2=p\xc2\xb2c\xc2\xb2+m\xc2\xb2c\xe2\x81\xb4".to_vec(),
145 integer: 9000,
146 path: PathBuf::from("src/lib.rs"),
147 },
148 DefaultedOpt::from_clap(&DefaultedOpt::clap().get_matches_from(&[
149 "test",
150 "-b",
151 "E²=p²c²+m²c⁴",
152 "-i",
153 "9000",
154 "-p",
155 "src/lib.rs",
156 ]))
157 );
158 }
159
160 #[derive(PartialEq, Debug)]
161 struct Foo(u8);
162
foo(value: u64) -> Foo163 fn foo(value: u64) -> Foo {
164 Foo(value as u8)
165 }
166
167 #[derive(StructOpt, PartialEq, Debug)]
168 struct Occurrences {
169 #[structopt(short = "s", long = "signed", parse(from_occurrences))]
170 signed: i32,
171
172 #[structopt(short = "l", parse(from_occurrences))]
173 little_signed: i8,
174
175 #[structopt(short = "u", parse(from_occurrences))]
176 unsigned: usize,
177
178 #[structopt(short = "r", parse(from_occurrences))]
179 little_unsigned: u8,
180
181 #[structopt(short = "c", long = "custom", parse(from_occurrences = "foo"))]
182 custom: Foo,
183 }
184
185 #[test]
test_parser_occurrences()186 fn test_parser_occurrences() {
187 assert_eq!(
188 Occurrences {
189 signed: 3,
190 little_signed: 1,
191 unsigned: 0,
192 little_unsigned: 4,
193 custom: Foo(5),
194 },
195 Occurrences::from_clap(&Occurrences::clap().get_matches_from(&[
196 "test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom",
197 ]))
198 );
199 }
200
201 #[test]
test_custom_bool()202 fn test_custom_bool() {
203 fn parse_bool(s: &str) -> Result<bool, String> {
204 match s {
205 "true" => Ok(true),
206 "false" => Ok(false),
207 _ => Err(format!("invalid bool {}", s)),
208 }
209 }
210 #[derive(StructOpt, PartialEq, Debug)]
211 struct Opt {
212 #[structopt(short = "d", parse(try_from_str = "parse_bool"))]
213 debug: bool,
214 #[structopt(
215 short = "v",
216 default_value = "false",
217 parse(try_from_str = "parse_bool")
218 )]
219 verbose: bool,
220 #[structopt(short = "t", parse(try_from_str = "parse_bool"))]
221 tribool: Option<bool>,
222 #[structopt(short = "b", parse(try_from_str = "parse_bool"))]
223 bitset: Vec<bool>,
224 }
225
226 assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
227 assert!(Opt::clap().get_matches_from_safe(&["test", "-d"]).is_err());
228 assert!(Opt::clap()
229 .get_matches_from_safe(&["test", "-dfoo"])
230 .is_err());
231 assert_eq!(
232 Opt {
233 debug: false,
234 verbose: false,
235 tribool: None,
236 bitset: vec![],
237 },
238 Opt::from_iter(&["test", "-dfalse"])
239 );
240 assert_eq!(
241 Opt {
242 debug: true,
243 verbose: false,
244 tribool: None,
245 bitset: vec![],
246 },
247 Opt::from_iter(&["test", "-dtrue"])
248 );
249 assert_eq!(
250 Opt {
251 debug: true,
252 verbose: false,
253 tribool: None,
254 bitset: vec![],
255 },
256 Opt::from_iter(&["test", "-dtrue", "-vfalse"])
257 );
258 assert_eq!(
259 Opt {
260 debug: true,
261 verbose: true,
262 tribool: None,
263 bitset: vec![],
264 },
265 Opt::from_iter(&["test", "-dtrue", "-vtrue"])
266 );
267 assert_eq!(
268 Opt {
269 debug: true,
270 verbose: false,
271 tribool: Some(false),
272 bitset: vec![],
273 },
274 Opt::from_iter(&["test", "-dtrue", "-tfalse"])
275 );
276 assert_eq!(
277 Opt {
278 debug: true,
279 verbose: false,
280 tribool: Some(true),
281 bitset: vec![],
282 },
283 Opt::from_iter(&["test", "-dtrue", "-ttrue"])
284 );
285 assert_eq!(
286 Opt {
287 debug: true,
288 verbose: false,
289 tribool: None,
290 bitset: vec![false, true, false, false],
291 },
292 Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"])
293 );
294 }
295