]> git.proxmox.com Git - proxmox-backup.git/blob - src/getopts.rs
cli/command.rs: add new type CliCommandMap
[proxmox-backup.git] / src / getopts.rs
1 use crate::api::schema::*;
2
3 use failure::*;
4
5 use serde_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 chars[0] == '-' {
23 let mut first = 1;
24
25 if chars[1] == '-' {
26 if length == 2 { return RawArgument::Separator; }
27 first = 2;
28 }
29
30 for start in first..length {
31 if chars[start] == '=' {
32 let name: String = chars[first..start].iter().collect();
33 let value: String = chars[start+1..length].iter().collect();
34 return RawArgument::Option { name, value: Some(value) }
35 }
36 }
37
38 let name: String = chars[first..].iter().collect();
39 return RawArgument::Option { name: name, value: None }
40 }
41 }
42
43 RawArgument::Argument { value: arg.to_string() }
44 }
45
46 pub fn parse_arguments(
47 args: &Vec<String>,
48 arg_param: &Vec<&'static str>,
49 schema: &ObjectSchema,
50 ) -> Result<(Value,Vec<String>), ParameterError> {
51
52 let mut errors = ParameterError::new();
53
54 let properties = &schema.properties;
55
56 let mut data: Vec<(String, String)> = vec![];
57 let mut rest: Vec<String> = vec![];
58
59 let mut pos = 0;
60
61 let mut skip = false;
62
63 while pos < args.len() {
64 if skip {
65 rest.push(args[pos].clone());
66 } else {
67 match parse_argument(&args[pos]) {
68 RawArgument::Separator => {
69 skip = true;
70 }
71 RawArgument::Option { name, value } => {
72 match value {
73 None => {
74 let mut want_bool = false;
75 let mut can_default = false;
76 if let Some((_optional, param_schema)) = properties.get::<str>(&name) {
77 if let Schema::Boolean(boolean_schema) = param_schema.as_ref() {
78 want_bool = true;
79 if let Some(default) = boolean_schema.default {
80 if default == false { can_default = true; }
81 } else {
82 can_default = true;
83 }
84 }
85 }
86
87 let mut next_is_argument = false;
88 let mut next_is_bool = false;
89
90 if (pos + 1) < args.len() {
91 let next = &args[pos+1];
92 if let RawArgument::Argument { value: _} = parse_argument(next) {
93 next_is_argument = true;
94 if let Ok(_) = parse_boolean(next) { next_is_bool = true; }
95 }
96 }
97
98 if want_bool {
99 if next_is_bool {
100 pos += 1;
101 data.push((name, args[pos].clone()));
102 } else if can_default {
103 data.push((name, "true".to_string()));
104 } else {
105 errors.push(format_err!("parameter '{}': {}", name,
106 "missing boolean value."));
107 }
108
109 } else {
110
111 if next_is_argument {
112 pos += 1;
113 data.push((name, args[pos].clone()));
114 } else {
115 errors.push(format_err!("parameter '{}': {}", name,
116 "missing parameter value."));
117 }
118 }
119 }
120 Some(v) => {
121 data.push((name, v));
122 }
123 }
124 }
125 RawArgument::Argument { value } => {
126 rest.push(value);
127 }
128 }
129 }
130
131 pos += 1;
132 }
133
134 for i in 0..arg_param.len() {
135 if rest.len() > i {
136 data.push((arg_param[i].to_string(), rest[i].clone()));
137 } else {
138 errors.push(format_err!("missing argument '{}'", arg_param[i]));
139 }
140 }
141
142 if errors.len() > 0 { return Err(errors); }
143
144 if arg_param.len() > 0 {
145 rest = rest[arg_param.len()..].to_vec();
146 }
147
148 let options = parse_parameter_strings(&data, schema, true)?;
149
150 Ok((options,rest))
151 }
152
153
154 #[test]
155 fn test_boolean_arg() {
156
157 let schema = ObjectSchema::new("Parameters:")
158 .required(
159 "enable", BooleanSchema::new("Enable")
160 );
161
162 let mut variants: Vec<(Vec<&str>, bool)> = vec![];
163 variants.push((vec!["-enable"], true));
164 variants.push((vec!["-enable=1"], true));
165 variants.push((vec!["-enable", "yes"], true));
166 variants.push((vec!["-enable", "Yes"], true));
167 variants.push((vec!["--enable", "1"], true));
168 variants.push((vec!["--enable", "ON"], true));
169 variants.push((vec!["--enable", "true"], true));
170
171 variants.push((vec!["--enable", "0"], false));
172 variants.push((vec!["--enable", "no"], false));
173 variants.push((vec!["--enable", "off"], false));
174 variants.push((vec!["--enable", "false"], false));
175
176 for (args, expect) in variants {
177 let string_args = args.iter().map(|s| s.to_string()).collect();
178 let res = parse_arguments(&string_args, &vec![], &schema);
179 assert!(res.is_ok());
180 if let Ok((options, rest)) = res {
181 assert!(options["enable"] == expect);
182 assert!(rest.len() == 0);
183 }
184 }
185 }
186
187 #[test]
188 fn test_argument_paramenter() {
189
190 let schema = ObjectSchema::new("Parameters:")
191 .required("enable", BooleanSchema::new("Enable."))
192 .required("storage", StringSchema::new("Storage."));
193
194 let args = vec!["-enable", "local"];
195 let string_args = args.iter().map(|s| s.to_string()).collect();
196 let res = parse_arguments(&string_args, &vec!["storage"], &schema);
197 assert!(res.is_ok());
198 if let Ok((options, rest)) = res {
199 assert!(options["enable"] == true);
200 assert!(options["storage"] == "local");
201 assert!(rest.len() == 0);
202 }
203 }