]> git.proxmox.com Git - proxmox-backup.git/blob - src/getopts.rs
getopt parser fixes and cleanups
[proxmox-backup.git] / src / getopts.rs
1 use crate::api::schema::*;
2
3 use failure::*;
4 use std::collections::HashMap;
5 use serde_json::{json, Value};
6
7 #[derive(Debug)]
8 enum RawArgument {
9 Separator,
10 Argument { value: String },
11 Option { name: String, value: Option<String> },
12 }
13
14 fn parse_argument(arg: &str) -> RawArgument {
15
16 let chars: Vec<char> = arg.chars().collect();
17
18 let length = chars.len();
19
20 if length >= 2 {
21
22 if length == 2 { return RawArgument::Separator; }
23
24 if chars[0] == '-' {
25 let first = if chars[1] == '-' { 2 } else { 1 };
26
27 for start in first..length {
28 if chars[start] == '=' {
29 let name: String = chars[first..start].iter().collect();
30 let value: String = chars[start+1..length].iter().collect();
31 return RawArgument::Option { name, value: Some(value) }
32 }
33 }
34
35 let name: String = chars[first..].iter().collect();
36 return RawArgument::Option { name: name, value: None }
37 }
38 }
39
40 RawArgument::Argument { value: arg.to_string() }
41 }
42
43 pub fn parse_arguments(
44 args: &Vec<String>,
45 schema: &Schema,
46 ) -> Result<(Value,Vec<String>), ParameterError> {
47
48 let mut errors = ParameterError::new();
49
50 let properties = match schema {
51 Schema::Object(ObjectSchema { properties, .. }) => properties,
52 _ => {
53 errors.push(format_err!("parse arguments failed - got strange parameters (expected object schema)."));
54 return Err(errors);
55 },
56 };
57
58 let mut data: Vec<(String, String)> = vec![];
59 let mut rest: Vec<String> = vec![];
60
61 let mut pos = 0;
62
63 let mut skip = false;
64
65 loop {
66 if skip {
67 rest.push(args[pos].clone());
68 } else {
69 match parse_argument(&args[pos]) {
70 RawArgument::Separator => {
71 skip = true;
72 }
73 RawArgument::Option { name, value } => {
74 match value {
75 None => {
76 let param_schema = properties.get::<str>(&name);
77 let (want_bool, can_default) = match param_schema {
78 Some(Schema::Boolean(boolean_schema)) => {
79 if let Some(default) = boolean_schema.default {
80 if default == true { (true, false); }
81 }
82 (true, true)
83 }
84 _ => (false, false),
85 };
86
87 if want_bool {
88
89 let mut next_is_bool = false;
90 if (pos + 1) < args.len() {
91 let next = &args[pos+1];
92 if let Ok(_) = parse_boolean(next) { next_is_bool = true; }
93 }
94
95 if next_is_bool {
96 pos += 1;
97 data.push((name, args[pos].clone()));
98 } else if can_default {
99 data.push((name, "true".to_string()));
100 } else {
101 errors.push(format_err!("parameter '{}': {}", name,
102 "missing boolean value."));
103 }
104
105 } else {
106
107 if (pos + 1) < args.len() {
108 pos += 1;
109 data.push((name, args[pos].clone()));
110 } else {
111 errors.push(format_err!("parameter '{}': {}", name,
112 "missing parameter value."));
113 }
114 }
115 }
116 Some(v) => {
117 data.push((name, v));
118 }
119 }
120 }
121 RawArgument::Argument { value } => {
122 rest.push(value);
123 }
124 }
125 }
126
127 pos += 1;
128 if pos >= args.len() { break; }
129 }
130
131 if errors.len() > 0 { return Err(errors); }
132
133 let options = parse_parameter_strings(&data, schema, true)?;
134
135 Ok((options,rest))
136 }
137
138
139 #[test]
140 fn test_boolean_arg() {
141
142 let schema = parameter!{enable => Boolean!{ optional => false }};
143
144 let mut variants: Vec<Vec<&str>> = vec![];
145 variants.push(vec!["-enable"]);
146 variants.push(vec!["-enable=1"]);
147 variants.push(vec!["-enable", "yes"]);
148 variants.push(vec!["--enable", "1"]);
149
150 for args in variants {
151 let string_args = args.iter().map(|s| s.to_string()).collect();
152 let res = parse_arguments(&string_args, &schema);
153 println!("RES: {:?}", res);
154 assert!(res.is_ok());
155 if let Ok((options, rest)) = res {
156 assert!(options["enable"] == true);
157 assert!(rest.len() == 0);
158 }
159 }
160
161 //Ok((options, rest)) => {
162
163 }