aboutsummaryrefslogtreecommitdiff
path: root/structopt/tests/custom-string-parsers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'structopt/tests/custom-string-parsers.rs')
-rw-r--r--structopt/tests/custom-string-parsers.rs306
1 files changed, 306 insertions, 0 deletions
diff --git a/structopt/tests/custom-string-parsers.rs b/structopt/tests/custom-string-parsers.rs
new file mode 100644
index 0000000..89070ed
--- /dev/null
+++ b/structopt/tests/custom-string-parsers.rs
@@ -0,0 +1,306 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use structopt::StructOpt;
+
+use std::ffi::{CString, OsStr, OsString};
+use std::num::ParseIntError;
+use std::path::PathBuf;
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct PathOpt {
+ #[structopt(short, long, parse(from_os_str))]
+ path: PathBuf,
+
+ #[structopt(short, default_value = "../", parse(from_os_str))]
+ default_path: PathBuf,
+
+ #[structopt(short, parse(from_os_str))]
+ vector_path: Vec<PathBuf>,
+
+ #[structopt(short, parse(from_os_str))]
+ option_path_1: Option<PathBuf>,
+
+ #[structopt(short = "q", parse(from_os_str))]
+ option_path_2: Option<PathBuf>,
+}
+
+#[test]
+fn test_path_opt_simple() {
+ assert_eq!(
+ PathOpt {
+ path: PathBuf::from("/usr/bin"),
+ default_path: PathBuf::from("../"),
+ vector_path: vec![
+ PathBuf::from("/a/b/c"),
+ PathBuf::from("/d/e/f"),
+ PathBuf::from("/g/h/i"),
+ ],
+ option_path_1: None,
+ option_path_2: Some(PathBuf::from("j.zip")),
+ },
+ PathOpt::from_clap(&PathOpt::clap().get_matches_from(&[
+ "test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q",
+ "j.zip",
+ ]))
+ );
+}
+
+fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
+ u64::from_str_radix(input, 16)
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct HexOpt {
+ #[structopt(short, parse(try_from_str = parse_hex))]
+ number: u64,
+}
+
+#[test]
+#[allow(clippy::unreadable_literal)]
+fn test_parse_hex() {
+ assert_eq!(
+ HexOpt { number: 5 },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"]))
+ );
+ assert_eq!(
+ HexOpt { number: 0xabcdef },
+ HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"]))
+ );
+
+ let err = HexOpt::clap()
+ .get_matches_from_safe(&["test", "-n", "gg"])
+ .unwrap_err();
+ assert!(err.message.contains("invalid digit found in string"), err);
+}
+
+fn custom_parser_1(_: &str) -> &'static str {
+ "A"
+}
+fn custom_parser_2(_: &str) -> Result<&'static str, u32> {
+ Ok("B")
+}
+fn custom_parser_3(_: &OsStr) -> &'static str {
+ "C"
+}
+fn custom_parser_4(_: &OsStr) -> Result<&'static str, OsString> {
+ Ok("D")
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct NoOpOpt {
+ #[structopt(short, parse(from_str = custom_parser_1))]
+ a: &'static str,
+ #[structopt(short, parse(try_from_str = custom_parser_2))]
+ b: &'static str,
+ #[structopt(short, parse(from_os_str = custom_parser_3))]
+ c: &'static str,
+ #[structopt(short, parse(try_from_os_str = custom_parser_4))]
+ d: &'static str,
+}
+
+#[test]
+fn test_every_custom_parser() {
+ assert_eq!(
+ NoOpOpt {
+ a: "A",
+ b: "B",
+ c: "C",
+ d: "D"
+ },
+ NoOpOpt::from_clap(
+ &NoOpOpt::clap().get_matches_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"])
+ )
+ );
+}
+
+// Note: can't use `Vec<u8>` directly, as structopt would instead look for
+// conversion function from `&str` to `u8`.
+type Bytes = Vec<u8>;
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct DefaultedOpt {
+ #[structopt(short, parse(from_str))]
+ bytes: Bytes,
+
+ #[structopt(short, parse(try_from_str))]
+ integer: u64,
+
+ #[structopt(short, parse(from_os_str))]
+ path: PathBuf,
+}
+
+#[test]
+fn test_parser_with_default_value() {
+ assert_eq!(
+ DefaultedOpt {
+ bytes: b"E\xc2\xb2=p\xc2\xb2c\xc2\xb2+m\xc2\xb2c\xe2\x81\xb4".to_vec(),
+ integer: 9000,
+ path: PathBuf::from("src/lib.rs"),
+ },
+ DefaultedOpt::from_clap(&DefaultedOpt::clap().get_matches_from(&[
+ "test",
+ "-b",
+ "E²=p²c²+m²c⁴",
+ "-i",
+ "9000",
+ "-p",
+ "src/lib.rs",
+ ]))
+ );
+}
+
+#[derive(PartialEq, Debug)]
+struct Foo(u8);
+
+fn foo(value: u64) -> Foo {
+ Foo(value as u8)
+}
+
+#[derive(StructOpt, PartialEq, Debug)]
+struct Occurrences {
+ #[structopt(short, long, parse(from_occurrences))]
+ signed: i32,
+
+ #[structopt(short, parse(from_occurrences))]
+ little_signed: i8,
+
+ #[structopt(short, parse(from_occurrences))]
+ unsigned: usize,
+
+ #[structopt(short = "r", parse(from_occurrences))]
+ little_unsigned: u8,
+
+ #[structopt(short, long, parse(from_occurrences = foo))]
+ custom: Foo,
+}
+
+#[test]
+fn test_parser_occurrences() {
+ assert_eq!(
+ Occurrences {
+ signed: 3,
+ little_signed: 1,
+ unsigned: 0,
+ little_unsigned: 4,
+ custom: Foo(5),
+ },
+ Occurrences::from_clap(&Occurrences::clap().get_matches_from(&[
+ "test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom",
+ ]))
+ );
+}
+
+#[test]
+fn test_custom_bool() {
+ fn parse_bool(s: &str) -> Result<bool, String> {
+ match s {
+ "true" => Ok(true),
+ "false" => Ok(false),
+ _ => Err(format!("invalid bool {}", s)),
+ }
+ }
+ #[derive(StructOpt, PartialEq, Debug)]
+ struct Opt {
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ debug: bool,
+ #[structopt(
+ short,
+ default_value = "false",
+ parse(try_from_str = parse_bool)
+ )]
+ verbose: bool,
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ tribool: Option<bool>,
+ #[structopt(short, parse(try_from_str = parse_bool))]
+ bitset: Vec<bool>,
+ }
+
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert!(Opt::clap().get_matches_from_safe(&["test", "-d"]).is_err());
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "-dfoo"])
+ .is_err());
+ assert_eq!(
+ Opt {
+ debug: false,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-vfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: true,
+ tribool: None,
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-vtrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: Some(false),
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-tfalse"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: Some(true),
+ bitset: vec![],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-ttrue"])
+ );
+ assert_eq!(
+ Opt {
+ debug: true,
+ verbose: false,
+ tribool: None,
+ bitset: vec![false, true, false, false],
+ },
+ Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"])
+ );
+}
+
+#[test]
+fn test_cstring() {
+ #[derive(StructOpt)]
+ struct Opt {
+ #[structopt(parse(try_from_str = CString::new))]
+ c_string: CString,
+ }
+ assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err());
+ assert_eq!(Opt::from_iter(&["test", "bla"]).c_string.to_bytes(), b"bla");
+ assert!(Opt::clap()
+ .get_matches_from_safe(&["test", "bla\0bla"])
+ .is_err());
+}