| // Copyright 2018 Guillaume Pinot (@TeXitoi) <[email protected]> |
| // |
| // 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()); |
| } |