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