2018-11-16 12:14:11 +00:00
|
|
|
use crate::api::schema::*;
|
|
|
|
|
|
|
|
use failure::*;
|
2018-11-20 15:54:29 +00:00
|
|
|
|
2018-11-30 10:15:26 +00:00
|
|
|
use serde_json::Value;
|
2018-11-16 12:14:11 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum RawArgument {
|
|
|
|
Separator,
|
|
|
|
Argument { value: String },
|
|
|
|
Option { name: String, value: Option<String> },
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_argument(arg: &str) -> RawArgument {
|
2018-12-28 12:14:13 +00:00
|
|
|
let bytes = arg.as_bytes();
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
let length = bytes.len();
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
if length < 2 || bytes[0] != b'-' {
|
|
|
|
return RawArgument::Argument {
|
|
|
|
value: arg.to_string(),
|
|
|
|
};
|
|
|
|
}
|
2018-11-18 08:34:43 +00:00
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
let mut first = 1;
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
if bytes[1] == b'-' {
|
|
|
|
if length == 2 {
|
|
|
|
return RawArgument::Separator;
|
|
|
|
}
|
|
|
|
first = 2;
|
|
|
|
}
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
for start in first..length {
|
|
|
|
if bytes[start] == b'=' {
|
|
|
|
// Since we take a &str, we know the contents of it are valid utf8.
|
|
|
|
// Since bytes[start] == b'=', we know the byte beginning at start is a single-byte
|
|
|
|
// code pointer. We also know that 'first' points exactly after a single-byte code
|
|
|
|
// point as it points to the first byte after a hyphen.
|
|
|
|
// Therefore we know arg[first..start] is valid utf-8, therefore it is safe to use
|
|
|
|
// get_unchecked() to speed things up.
|
|
|
|
return RawArgument::Option {
|
|
|
|
name: unsafe { arg.get_unchecked(first..start).to_string() },
|
|
|
|
value: Some(unsafe { arg.get_unchecked((start + 1)..).to_string() }),
|
|
|
|
};
|
2018-11-17 09:02:35 +00:00
|
|
|
}
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 12:14:13 +00:00
|
|
|
return RawArgument::Option {
|
|
|
|
name: unsafe { arg.get_unchecked(first..).to_string() },
|
|
|
|
value: None,
|
|
|
|
};
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 13:04:16 +00:00
|
|
|
pub fn parse_arguments<T: AsRef<str>>(
|
|
|
|
args: &[T],
|
2018-11-30 11:10:26 +00:00
|
|
|
arg_param: &Vec<&'static str>,
|
2018-11-18 07:46:26 +00:00
|
|
|
schema: &ObjectSchema,
|
2018-12-28 13:20:45 +00:00
|
|
|
) -> Result<(Value, Vec<String>), ParameterError> {
|
2018-11-17 08:57:26 +00:00
|
|
|
let mut errors = ParameterError::new();
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-11-18 07:46:26 +00:00
|
|
|
let properties = &schema.properties;
|
2018-11-16 12:14:11 +00:00
|
|
|
|
|
|
|
let mut data: Vec<(String, String)> = vec![];
|
|
|
|
let mut rest: Vec<String> = vec![];
|
|
|
|
|
|
|
|
let mut pos = 0;
|
|
|
|
|
2018-11-18 13:57:18 +00:00
|
|
|
while pos < args.len() {
|
2018-12-28 13:04:16 +00:00
|
|
|
match parse_argument(args[pos].as_ref()) {
|
2018-12-28 12:54:18 +00:00
|
|
|
RawArgument::Separator => {
|
2018-12-28 12:59:44 +00:00
|
|
|
break;
|
2018-12-28 12:54:18 +00:00
|
|
|
}
|
2018-12-28 13:15:09 +00:00
|
|
|
RawArgument::Option { name, value } => match value {
|
|
|
|
None => {
|
|
|
|
let mut want_bool = false;
|
|
|
|
let mut can_default = false;
|
2019-01-19 11:53:07 +00:00
|
|
|
if let Some((_optional, param_schema)) = properties.get::<str>(&name) {
|
2018-12-28 13:15:09 +00:00
|
|
|
if let Schema::Boolean(boolean_schema) = param_schema.as_ref() {
|
|
|
|
want_bool = true;
|
|
|
|
if let Some(default) = boolean_schema.default {
|
2018-12-28 13:24:24 +00:00
|
|
|
if default == false {
|
|
|
|
can_default = true;
|
|
|
|
}
|
2018-12-28 13:15:09 +00:00
|
|
|
} else {
|
|
|
|
can_default = true;
|
2018-11-20 15:54:29 +00:00
|
|
|
}
|
2018-12-28 12:54:18 +00:00
|
|
|
}
|
2018-12-28 13:15:09 +00:00
|
|
|
}
|
2018-11-17 10:28:26 +00:00
|
|
|
|
2018-12-28 13:15:09 +00:00
|
|
|
let mut next_is_argument = false;
|
|
|
|
let mut next_is_bool = false;
|
2018-11-17 10:28:26 +00:00
|
|
|
|
2018-12-28 13:15:09 +00:00
|
|
|
if (pos + 1) < args.len() {
|
2018-12-28 13:20:45 +00:00
|
|
|
let next = args[pos + 1].as_ref();
|
|
|
|
if let RawArgument::Argument { value: _ } = parse_argument(next) {
|
2018-12-28 13:15:09 +00:00
|
|
|
next_is_argument = true;
|
2018-12-28 13:24:24 +00:00
|
|
|
if let Ok(_) = parse_boolean(next) {
|
|
|
|
next_is_bool = true;
|
|
|
|
}
|
2018-12-28 12:54:18 +00:00
|
|
|
}
|
2018-12-28 13:15:09 +00:00
|
|
|
}
|
2018-11-17 10:28:26 +00:00
|
|
|
|
2018-12-28 13:15:09 +00:00
|
|
|
if want_bool {
|
|
|
|
if next_is_bool {
|
|
|
|
pos += 1;
|
|
|
|
data.push((name, args[pos].as_ref().to_string()));
|
|
|
|
} else if can_default {
|
2018-12-28 13:20:45 +00:00
|
|
|
data.push((name, "true".to_string()));
|
2018-12-28 12:54:18 +00:00
|
|
|
} else {
|
2018-12-28 13:15:09 +00:00
|
|
|
errors.push(format_err!("parameter '{}': {}", name,
|
|
|
|
"missing boolean value."));
|
|
|
|
}
|
2018-12-28 12:54:18 +00:00
|
|
|
|
2018-12-28 13:15:09 +00:00
|
|
|
} else {
|
|
|
|
|
|
|
|
if next_is_argument {
|
|
|
|
pos += 1;
|
|
|
|
data.push((name, args[pos].as_ref().to_string()));
|
|
|
|
} else {
|
|
|
|
errors.push(format_err!("parameter '{}': {}", name,
|
|
|
|
"missing parameter value."));
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
2018-12-28 12:54:18 +00:00
|
|
|
}
|
2018-12-28 13:15:09 +00:00
|
|
|
}
|
|
|
|
Some(v) => {
|
|
|
|
data.push((name, v));
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
2018-12-28 13:25:05 +00:00
|
|
|
},
|
2018-12-28 12:54:18 +00:00
|
|
|
RawArgument::Argument { value } => {
|
|
|
|
rest.push(value);
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += 1;
|
|
|
|
}
|
|
|
|
|
2018-12-28 13:04:16 +00:00
|
|
|
rest.reserve(args.len() - pos);
|
|
|
|
for i in &args[pos..] {
|
|
|
|
rest.push(i.as_ref().to_string());
|
|
|
|
}
|
2018-12-28 12:59:44 +00:00
|
|
|
|
2018-11-18 09:09:13 +00:00
|
|
|
for i in 0..arg_param.len() {
|
|
|
|
if rest.len() > i {
|
2018-11-30 11:10:26 +00:00
|
|
|
data.push((arg_param[i].to_string(), rest[i].clone()));
|
2018-11-18 09:09:13 +00:00
|
|
|
} else {
|
|
|
|
errors.push(format_err!("missing argument '{}'", arg_param[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 13:24:24 +00:00
|
|
|
if errors.len() > 0 {
|
|
|
|
return Err(errors);
|
|
|
|
}
|
2018-11-16 12:14:11 +00:00
|
|
|
|
2018-11-18 09:09:13 +00:00
|
|
|
if arg_param.len() > 0 {
|
|
|
|
rest = rest[arg_param.len()..].to_vec();
|
|
|
|
}
|
|
|
|
|
2018-11-17 08:57:26 +00:00
|
|
|
let options = parse_parameter_strings(&data, schema, true)?;
|
|
|
|
|
2018-12-28 13:20:45 +00:00
|
|
|
Ok((options, rest))
|
2018-11-16 12:14:11 +00:00
|
|
|
}
|
2018-11-17 10:28:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_boolean_arg() {
|
2018-11-23 10:34:15 +00:00
|
|
|
let schema = ObjectSchema::new("Parameters:")
|
|
|
|
.required(
|
|
|
|
"enable", BooleanSchema::new("Enable")
|
|
|
|
);
|
2018-11-17 10:28:26 +00:00
|
|
|
|
2018-11-18 08:34:43 +00:00
|
|
|
let mut variants: Vec<(Vec<&str>, bool)> = vec![];
|
|
|
|
variants.push((vec!["-enable"], true));
|
|
|
|
variants.push((vec!["-enable=1"], true));
|
|
|
|
variants.push((vec!["-enable", "yes"], true));
|
|
|
|
variants.push((vec!["-enable", "Yes"], true));
|
|
|
|
variants.push((vec!["--enable", "1"], true));
|
|
|
|
variants.push((vec!["--enable", "ON"], true));
|
|
|
|
variants.push((vec!["--enable", "true"], true));
|
|
|
|
|
|
|
|
variants.push((vec!["--enable", "0"], false));
|
|
|
|
variants.push((vec!["--enable", "no"], false));
|
|
|
|
variants.push((vec!["--enable", "off"], false));
|
|
|
|
variants.push((vec!["--enable", "false"], false));
|
|
|
|
|
|
|
|
for (args, expect) in variants {
|
2018-12-28 13:04:16 +00:00
|
|
|
let res = parse_arguments(&args, &vec![], &schema);
|
2018-11-17 10:28:26 +00:00
|
|
|
assert!(res.is_ok());
|
|
|
|
if let Ok((options, rest)) = res {
|
2018-11-18 08:34:43 +00:00
|
|
|
assert!(options["enable"] == expect);
|
2018-11-17 10:28:26 +00:00
|
|
|
assert!(rest.len() == 0);
|
|
|
|
}
|
|
|
|
}
|
2018-11-18 09:09:13 +00:00
|
|
|
}
|
2018-11-17 10:28:26 +00:00
|
|
|
|
2018-11-18 09:09:13 +00:00
|
|
|
#[test]
|
|
|
|
fn test_argument_paramenter() {
|
2018-11-23 10:34:15 +00:00
|
|
|
let schema = ObjectSchema::new("Parameters:")
|
2018-11-23 12:18:41 +00:00
|
|
|
.required("enable", BooleanSchema::new("Enable."))
|
|
|
|
.required("storage", StringSchema::new("Storage."));
|
2018-11-18 09:09:13 +00:00
|
|
|
|
|
|
|
let args = vec!["-enable", "local"];
|
2018-12-28 13:04:16 +00:00
|
|
|
let res = parse_arguments(&args, &vec!["storage"], &schema);
|
2018-11-18 09:09:13 +00:00
|
|
|
assert!(res.is_ok());
|
|
|
|
if let Ok((options, rest)) = res {
|
|
|
|
assert!(options["enable"] == true);
|
|
|
|
assert!(options["storage"] == "local");
|
|
|
|
assert!(rest.len() == 0);
|
|
|
|
}
|
2018-11-17 10:28:26 +00:00
|
|
|
}
|