• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]
test_path_opt_simple()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 
parse_hex(input: &str) -> Result<u64, ParseIntError>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)]
test_parse_hex()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 
custom_parser_1(_: &str) -> &'static str82 fn custom_parser_1(_: &str) -> &'static str {
83     "A"
84 }
custom_parser_2(_: &str) -> Result<&'static str, u32>85 fn custom_parser_2(_: &str) -> Result<&'static str, u32> {
86     Ok("B")
87 }
custom_parser_3(_: &OsStr) -> &'static str88 fn custom_parser_3(_: &OsStr) -> &'static str {
89     "C"
90 }
custom_parser_4(_: &OsStr) -> Result<&'static str, OsString>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]
test_every_custom_parser()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]
test_parser_with_default_value()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 
foo(value: u64) -> Foo161 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]
test_parser_occurrences()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]
test_custom_bool()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]
test_cstring()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 }
307