use ahash::AHashMap;
use serde::Deserialize;
use crate::{
ArgvMap, Docopt, Error,
Value::{self, Plain, Switch},
};
fn get_args(doc: &str, argv: &[&'static str]) -> ArgvMap {
let dopt = match Docopt::new(doc) {
Err(err) => panic!("Invalid usage: {}", err),
Ok(dopt) => dopt,
};
match dopt.argv(["cmd"].iter().chain(argv.iter())).parse() {
Err(err) => panic!("{}", err),
Ok(vals) => vals,
}
}
fn map_from_alist(alist: Vec<(&'static str, Value)>) -> AHashMap<String, Value> {
alist.into_iter().map(|(k, v)| (k.to_string(), v)).collect()
}
fn same_args(expected: &AHashMap<String, Value>, got: &ArgvMap) {
for (k, ve) in expected.iter() {
match got.map.find(k) {
None => panic!("EXPECTED has '{}' but GOT does not.", k),
Some(vg) => {
assert!(ve == vg, "{}: EXPECTED = '{:?}' != '{:?}' = GOT", k, ve, vg)
}
}
}
for (k, vg) in got.map.iter() {
match got.map.find(k) {
None => panic!("GOT has '{}' but EXPECTED does not.", k),
Some(ve) => {
assert!(vg == ve, "{}: GOT = '{:?}' != '{:?}' = EXPECTED", k, vg, ve)
}
}
}
}
macro_rules! test_expect(
($name:ident, $doc:expr, $args:expr, $expected:expr) => (
#[test]
fn $name() {
let vals = get_args($doc, $args);
let expected = map_from_alist($expected);
same_args(&expected, &vals);
}
);
);
macro_rules! test_user_error(
($name:ident, $doc:expr, $args:expr) => (
#[test]
#[should_panic]
fn $name() { get_args($doc, $args); }
);
);
test_expect!(
test_issue_13,
"Usage: prog file <file>",
&["file", "file"],
vec![
("file", Switch(true)),
("<file>", Plain(Some("file".to_string())))
]
);
test_expect!(
test_issue_129,
"Usage: prog [options]
Options:
--foo ARG Foo foo.",
&["--foo=a b"],
vec![("--foo", Plain(Some("a b".into())))]
);
#[test]
fn regression_issue_12() {
const USAGE: &str = "
Usage:
whisper info <file>
whisper update <file> <timestamp> <value>
whisper mark <file> <value>
";
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Args {
arg_file: String,
cmd_info: bool,
cmd_update: bool,
arg_timestamp: u64,
arg_value: f64,
}
let dopt: Args = Docopt::new(USAGE)
.unwrap()
.argv(["whisper", "mark", "./p/blah", "100"])
.deserialize()
.unwrap();
assert_eq!(dopt.arg_timestamp, 0);
}
#[test]
fn regression_issue_195() {
const USAGE: &str = "
Usage:
slow [-abcdefghijklmnopqrs...]
";
let argv = &["slow", "-abcdefghijklmnopqrs"];
let dopt: Docopt = Docopt::new(USAGE).unwrap().argv(argv);
dopt.parse().unwrap();
}
#[test]
fn regression_issue_219() {
#[derive(Deserialize)]
struct Args {
arg_type: Vec<String>,
arg_param: Vec<String>,
}
const USAGE: &str = "
Usage:
encode [-v <type> <param>]...
";
let argv = &["encode", "-v", "bool", "true", "string", "foo"];
let args: Args = Docopt::new(USAGE)
.unwrap()
.argv(argv)
.deserialize()
.unwrap();
assert_eq!(args.arg_type, vec!["bool".to_owned(), "string".to_owned()]);
assert_eq!(args.arg_param, vec!["true".to_owned(), "foo".to_owned()]);
}
#[test]
fn test_unit_struct() {
const USAGE: &str = "
Usage:
cargo version [options]
Options:
-h, --help Print this message
";
#[derive(Deserialize)]
struct Options;
let argv = &["cargo", "version"];
let dopt: Result<Options, Error> = Docopt::new(USAGE).unwrap().argv(argv).deserialize();
assert!(dopt.is_ok());
}
#[test]
#[cfg(unix)]
fn test_non_utf8_args_lossy_conversion() {
use std::os::unix::ffi::OsStringExt;
const USAGE: &str = "
Usage:
prog <input> <output>
";
#[derive(Deserialize, Debug)]
struct Args {
arg_input: String,
arg_output: String,
}
let invalid_utf8_input = std::ffi::OsString::from_vec(vec![0xFF, 0xFE, 0xFD]);
let valid_output = std::ffi::OsString::from("output.txt");
let input_str = invalid_utf8_input.to_string_lossy().into_owned();
let output_str = valid_output.to_string_lossy().into_owned();
assert!(
input_str.contains('�'),
"Non-UTF-8 should convert to replacement character"
);
let argv = vec!["prog", &input_str, &output_str];
let args: Args = Docopt::new(USAGE)
.unwrap()
.argv(&argv)
.deserialize()
.unwrap();
assert_eq!(args.arg_input, input_str);
assert_eq!(args.arg_output, "output.txt");
assert!(args.arg_input.contains('�'));
}
mod suggestions;
mod testcases;