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