4 use proxmox
::api
::schema
::*;
9 Argument { value: String }
,
10 Option { name: String, value: Option<String> }
,
13 fn parse_argument(arg
: &str) -> RawArgument
{
14 let bytes
= arg
.as_bytes();
16 let length
= bytes
.len();
18 if length
< 2 || bytes
[0] != b'
-'
{
19 return RawArgument
::Argument
{
20 value
: arg
.to_string(),
28 return RawArgument
::Separator
;
33 for start
in first
..length
{
34 if bytes
[start
] == b'
='
{
35 // Since we take a &str, we know the contents of it are valid utf8.
36 // Since bytes[start] == b'=', we know the byte beginning at start is a single-byte
37 // code pointer. We also know that 'first' points exactly after a single-byte code
38 // point as it points to the first byte after a hyphen.
39 // Therefore we know arg[first..start] is valid utf-8, therefore it is safe to use
40 // get_unchecked() to speed things up.
41 return RawArgument
::Option
{
42 name
: unsafe { arg.get_unchecked(first..start).to_string() }
,
43 value
: Some(unsafe { arg.get_unchecked((start + 1)..).to_string() }
),
49 name
: unsafe { arg.get_unchecked(first..).to_string() }
,
54 /// parse as many arguments as possible into a Vec<String, String>. This does not
55 /// verify the schema.
56 /// Returns parsed data and the rest as separate array
57 pub (crate) fn parse_argument_list
<T
: AsRef
<str>>(
59 schema
: &ObjectSchema
,
60 errors
: &mut ParameterError
,
61 ) -> (Vec
<(String
, String
)>, Vec
<String
>) {
63 let mut data
: Vec
<(String
, String
)> = vec
![];
64 let mut rest
: Vec
<String
> = vec
![];
68 while pos
< args
.len() {
69 match parse_argument(args
[pos
].as_ref()) {
70 RawArgument
::Separator
=> {
73 RawArgument
::Option { name, value }
=> match value
{
75 let mut want_bool
= false;
76 let mut can_default
= false;
77 if let Some((_optional
, param_schema
)) = schema
.lookup(&name
) {
78 if let Schema
::Boolean(boolean_schema
) = param_schema
{
80 if let Some(default) = boolean_schema
.default {
90 let mut next_is_argument
= false;
91 let mut next_is_bool
= false;
93 if (pos
+ 1) < args
.len() {
94 let next
= args
[pos
+ 1].as_ref();
95 if let RawArgument
::Argument { .. }
= parse_argument(next
) {
96 next_is_argument
= true;
97 if let Ok(_
) = parse_boolean(next
) {
106 data
.push((name
, args
[pos
].as_ref().to_string()));
107 } else if can_default
{
108 data
.push((name
, "true".to_string()));
110 errors
.push(format_err
!("parameter '{}': {}", name
,
111 "missing boolean value."));
114 } else if next_is_argument
{
116 data
.push((name
, args
[pos
].as_ref().to_string()));
118 errors
.push(format_err
!("parameter '{}': {}", name
,
119 "missing parameter value."));
123 data
.push((name
, v
));
126 RawArgument
::Argument { value }
=> {
134 rest
.reserve(args
.len() - pos
);
135 for i
in &args
[pos
..] {
136 rest
.push(i
.as_ref().to_string());
142 /// Parses command line arguments using a `Schema`
144 /// Returns parsed options as json object, together with the
145 /// list of additional command line arguments.
146 pub fn parse_arguments
<T
: AsRef
<str>>(
148 arg_param
: &Vec
<&'
static str>,
149 schema
: &ObjectSchema
,
150 ) -> Result
<(Value
, Vec
<String
>), ParameterError
> {
151 let mut errors
= ParameterError
::new();
153 // first check if all arg_param exists in schema
155 let mut last_arg_param_is_optional
= false;
156 let mut last_arg_param_is_array
= false;
158 for i
in 0..arg_param
.len() {
159 let name
= arg_param
[i
];
160 if let Some((optional
, param_schema
)) = schema
.lookup(&name
) {
161 if i
== arg_param
.len() -1 {
162 last_arg_param_is_optional
= optional
;
163 if let Schema
::Array(_
) = param_schema
{
164 last_arg_param_is_array
= true;
167 panic
!("positional argument '{}' may not be optional", name
);
170 panic
!("no such property '{}' in schema", name
);
174 let (mut data
, mut rest
) = parse_argument_list(args
, schema
, &mut errors
);
176 for i
in 0..arg_param
.len() {
178 let name
= arg_param
[i
];
179 let is_last_arg_param
= i
== (arg_param
.len() - 1);
182 if !(is_last_arg_param
&& last_arg_param_is_optional
) {
183 errors
.push(format_err
!("missing argument '{}'", name
));
185 } else if is_last_arg_param
&& last_arg_param_is_array
{
187 data
.push((name
.to_string(), value
));
191 data
.push((name
.to_string(), rest
.remove(0)));
195 if errors
.len() > 0 {
199 let options
= parse_parameter_strings(&data
, schema
, true)?
;
205 fn test_boolean_arg() {
207 const PARAMETERS
: ObjectSchema
= ObjectSchema
::new(
209 &[ ("enable", false, &BooleanSchema
::new("Enable").schema()) ],
212 let mut variants
: Vec
<(Vec
<&str>, bool
)> = vec
![];
213 variants
.push((vec
!["-enable"], true));
214 variants
.push((vec
!["-enable=1"], true));
215 variants
.push((vec
!["-enable", "yes"], true));
216 variants
.push((vec
!["-enable", "Yes"], true));
217 variants
.push((vec
!["--enable", "1"], true));
218 variants
.push((vec
!["--enable", "ON"], true));
219 variants
.push((vec
!["--enable", "true"], true));
221 variants
.push((vec
!["--enable", "0"], false));
222 variants
.push((vec
!["--enable", "no"], false));
223 variants
.push((vec
!["--enable", "off"], false));
224 variants
.push((vec
!["--enable", "false"], false));
226 for (args
, expect
) in variants
{
227 let res
= parse_arguments(&args
, &vec
![], &PARAMETERS
);
228 assert
!(res
.is_ok());
229 if let Ok((options
, rest
)) = res
{
230 assert
!(options
["enable"] == expect
);
231 assert
!(rest
.len() == 0);
237 fn test_argument_paramenter() {
239 const PARAMETERS
: ObjectSchema
= ObjectSchema
::new(
242 ("enable", false, &BooleanSchema
::new("Enable.").schema()),
243 ("storage", false, &StringSchema
::new("Storage.").schema()),
247 let args
= vec
!["-enable", "local"];
248 let res
= parse_arguments(&args
, &vec
!["storage"], &PARAMETERS
);
249 assert
!(res
.is_ok());
250 if let Ok((options
, rest
)) = res
{
251 assert
!(options
["enable"] == true);
252 assert
!(options
["storage"] == "local");
253 assert
!(rest
.len() == 0);