1 use crate::api_schema
::*;
10 Argument { value: String }
,
11 Option { name: String, value: Option<String> }
,
14 fn parse_argument(arg
: &str) -> RawArgument
{
15 let bytes
= arg
.as_bytes();
17 let length
= bytes
.len();
19 if length
< 2 || bytes
[0] != b'
-'
{
20 return RawArgument
::Argument
{
21 value
: arg
.to_string(),
29 return RawArgument
::Separator
;
34 for start
in first
..length
{
35 if bytes
[start
] == b'
='
{
36 // Since we take a &str, we know the contents of it are valid utf8.
37 // Since bytes[start] == b'=', we know the byte beginning at start is a single-byte
38 // code pointer. We also know that 'first' points exactly after a single-byte code
39 // point as it points to the first byte after a hyphen.
40 // Therefore we know arg[first..start] is valid utf-8, therefore it is safe to use
41 // get_unchecked() to speed things up.
42 return RawArgument
::Option
{
43 name
: unsafe { arg.get_unchecked(first..start).to_string() }
,
44 value
: Some(unsafe { arg.get_unchecked((start + 1)..).to_string() }
),
50 name
: unsafe { arg.get_unchecked(first..).to_string() }
,
55 /// parse as many arguments as possible into a Vec<String, String>. This does not
56 /// verify the schema.
57 /// Returns parsed data and the rest as separate array
58 pub (crate) fn parse_argument_list
<T
: AsRef
<str>>(
60 schema
: &ObjectSchema
,
61 errors
: &mut ParameterError
,
62 ) -> (Vec
<(String
, String
)>, Vec
<String
>) {
64 let mut data
: Vec
<(String
, String
)> = vec
![];
65 let mut rest
: Vec
<String
> = vec
![];
69 let properties
= &schema
.properties
;
71 while pos
< args
.len() {
72 match parse_argument(args
[pos
].as_ref()) {
73 RawArgument
::Separator
=> {
76 RawArgument
::Option { name, value }
=> match value
{
78 let mut want_bool
= false;
79 let mut can_default
= false;
80 if let Some((_optional
, param_schema
)) = properties
.get
::<str>(&name
) {
81 if let Schema
::Boolean(boolean_schema
) = param_schema
.as_ref() {
83 if let Some(default) = boolean_schema
.default {
93 let mut next_is_argument
= false;
94 let mut next_is_bool
= false;
96 if (pos
+ 1) < args
.len() {
97 let next
= args
[pos
+ 1].as_ref();
98 if let RawArgument
::Argument { .. }
= parse_argument(next
) {
99 next_is_argument
= true;
100 if let Ok(_
) = parse_boolean(next
) {
109 data
.push((name
, args
[pos
].as_ref().to_string()));
110 } else if can_default
{
111 data
.push((name
, "true".to_string()));
113 errors
.push(format_err
!("parameter '{}': {}", name
,
114 "missing boolean value."));
117 } else if next_is_argument
{
119 data
.push((name
, args
[pos
].as_ref().to_string()));
121 errors
.push(format_err
!("parameter '{}': {}", name
,
122 "missing parameter value."));
126 data
.push((name
, v
));
129 RawArgument
::Argument { value }
=> {
137 rest
.reserve(args
.len() - pos
);
138 for i
in &args
[pos
..] {
139 rest
.push(i
.as_ref().to_string());
145 /// Parses command line arguments using a `Schema`
147 /// Returns parsed options as json object, together with the
148 /// list of additional command line arguments.
149 pub fn parse_arguments
<T
: AsRef
<str>>(
151 arg_param
: &Vec
<&'
static str>,
152 schema
: &ObjectSchema
,
153 ) -> Result
<(Value
, Vec
<String
>), ParameterError
> {
154 let mut errors
= ParameterError
::new();
156 let properties
= &schema
.properties
;
158 // first check if all arg_param exists in schema
160 let mut last_arg_param_is_optional
= false;
161 let mut last_arg_param_is_array
= false;
163 for i
in 0..arg_param
.len() {
164 let name
= arg_param
[i
];
165 if let Some((optional
, param_schema
)) = properties
.get
::<str>(&name
) {
166 if i
== arg_param
.len() -1 {
167 last_arg_param_is_optional
= *optional
;
168 if let Schema
::Array(_
) = param_schema
.as_ref() {
169 last_arg_param_is_array
= true;
171 } else if *optional
{
172 panic
!("positional argument '{}' may not be optional", name
);
175 panic
!("no such property '{}' in schema", name
);
179 let (mut data
, mut rest
) = parse_argument_list(args
, schema
, &mut errors
);
181 for i
in 0..arg_param
.len() {
183 let name
= arg_param
[i
];
184 let is_last_arg_param
= i
== (arg_param
.len() - 1);
187 if !(is_last_arg_param
&& last_arg_param_is_optional
) {
188 errors
.push(format_err
!("missing argument '{}'", name
));
190 } else if is_last_arg_param
&& last_arg_param_is_array
{
192 data
.push((name
.to_string(), value
));
196 data
.push((name
.to_string(), rest
.remove(0)));
200 if errors
.len() > 0 {
204 let options
= parse_parameter_strings(&data
, schema
, true)?
;
210 fn test_boolean_arg() {
211 let schema
= ObjectSchema
::new("Parameters:")
213 "enable", BooleanSchema
::new("Enable")
216 let mut variants
: Vec
<(Vec
<&str>, bool
)> = vec
![];
217 variants
.push((vec
!["-enable"], true));
218 variants
.push((vec
!["-enable=1"], true));
219 variants
.push((vec
!["-enable", "yes"], true));
220 variants
.push((vec
!["-enable", "Yes"], true));
221 variants
.push((vec
!["--enable", "1"], true));
222 variants
.push((vec
!["--enable", "ON"], true));
223 variants
.push((vec
!["--enable", "true"], true));
225 variants
.push((vec
!["--enable", "0"], false));
226 variants
.push((vec
!["--enable", "no"], false));
227 variants
.push((vec
!["--enable", "off"], false));
228 variants
.push((vec
!["--enable", "false"], false));
230 for (args
, expect
) in variants
{
231 let res
= parse_arguments(&args
, &vec
![], &schema
);
232 assert
!(res
.is_ok());
233 if let Ok((options
, rest
)) = res
{
234 assert
!(options
["enable"] == expect
);
235 assert
!(rest
.len() == 0);
241 fn test_argument_paramenter() {
242 let schema
= ObjectSchema
::new("Parameters:")
243 .required("enable", BooleanSchema
::new("Enable."))
244 .required("storage", StringSchema
::new("Storage."));
246 let args
= vec
!["-enable", "local"];
247 let res
= parse_arguments(&args
, &vec
!["storage"], &schema
);
248 assert
!(res
.is_ok());
249 if let Ok((options
, rest
)) = res
{
250 assert
!(options
["enable"] == true);
251 assert
!(options
["storage"] == "local");
252 assert
!(rest
.len() == 0);