Import structopt-0.3.14

Change-Id: I03e7eaf9f442092701ce3175cb78c8ea2237f616
diff --git a/tests/custom-string-parsers.rs b/tests/custom-string-parsers.rs
new file mode 100644
index 0000000..89070ed
--- /dev/null
+++ b/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());
+}