]> git.proxmox.com Git - proxmox-backup.git/blob - src/api/schema.rs
remove some rather inconvenient debug output
[proxmox-backup.git] / src / api / schema.rs
1 use failure::*;
2 use std::collections::HashMap;
3 use serde_json::{json, Value};
4 use url::form_urlencoded;
5 use regex::Regex;
6 use std::fmt;
7 use std::sync::Arc;
8
9 #[derive(Debug, Fail)]
10 pub struct ParameterError {
11 error_list: Vec<Error>,
12 }
13
14 impl ParameterError {
15
16 pub fn new() -> Self {
17 Self { error_list: vec![] }
18 }
19
20 pub fn push(&mut self, value: Error) {
21 self.error_list.push(value);
22 }
23
24 pub fn len(&self) -> usize {
25 self.error_list.len()
26 }
27 }
28
29 impl fmt::Display for ParameterError {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 let msg = self.error_list.iter().fold(String::from(""), |acc, item| {
32 acc + &item.to_string() + "\n"
33 });
34
35 write!(f, "{}", msg)
36 }
37 }
38
39 #[derive(Debug)]
40 pub struct BooleanSchema {
41 pub description: &'static str,
42 pub default: Option<bool>,
43 }
44
45 impl BooleanSchema {
46
47 pub fn new(description: &'static str) -> Self {
48 BooleanSchema {
49 description: description,
50 default: None,
51 }
52 }
53
54 pub fn default(mut self, default: bool) -> Self {
55 self.default = Some(default);
56 self
57 }
58 }
59
60 #[derive(Debug)]
61 pub struct IntegerSchema {
62 pub description: &'static str,
63 pub minimum: Option<isize>,
64 pub maximum: Option<isize>,
65 pub default: Option<isize>,
66 }
67
68 impl IntegerSchema {
69
70 pub fn new(description: &'static str) -> Self {
71 IntegerSchema {
72 description: description,
73 default: None,
74 minimum: None,
75 maximum: None,
76 }
77 }
78
79 pub fn default(mut self, default: isize) -> Self {
80 self.default = Some(default);
81 self
82 }
83
84 pub fn minimum(mut self, minimum: isize) -> Self {
85 self.minimum = Some(minimum);
86 self
87 }
88
89 pub fn maximum(mut self, maximium: isize) -> Self {
90 self.maximum = Some(maximium);
91 self
92 }
93
94 fn check_constraints(&self, value: isize) -> Result<(), Error> {
95
96 if let Some(minimum) = self.minimum {
97 if value < minimum {
98 bail!("value must have a minimum value of {}", minimum);
99 }
100 }
101
102 if let Some(maximum) = self.maximum {
103 if value > maximum {
104 bail!("value must have a maximum value of {}", maximum);
105 }
106 }
107
108 Ok(())
109 }
110 }
111
112
113 #[derive(Debug)]
114 pub struct StringSchema {
115 pub description: &'static str,
116 pub default: Option<&'static str>,
117 pub min_length: Option<usize>,
118 pub max_length: Option<usize>,
119 pub format: Option<Arc<ApiStringFormat>>,
120 }
121
122 impl StringSchema {
123
124 pub fn new(description: &'static str) -> Self {
125 StringSchema {
126 description: description,
127 default: None,
128 min_length: None,
129 max_length: None,
130 format: None,
131 }
132 }
133
134 pub fn default(mut self, text: &'static str) -> Self {
135 self.default = Some(text);
136 self
137 }
138
139 pub fn format(mut self, format: Arc<ApiStringFormat>) -> Self {
140 self.format = Some(format);
141 self
142 }
143
144 pub fn min_length(mut self, min_length: usize) -> Self {
145 self.min_length = Some(min_length);
146 self
147 }
148
149 pub fn max_length(mut self, max_length: usize) -> Self {
150 self.max_length = Some(max_length);
151 self
152 }
153
154 fn check_length(&self, length: usize) -> Result<(), Error> {
155
156 if let Some(min_length) = self.min_length {
157 if length < min_length {
158 bail!("value must be at least {} characters long", min_length);
159 }
160 }
161
162 if let Some(max_length) = self.max_length {
163 if length > max_length {
164 bail!("value may only be {} characters long", max_length);
165 }
166 }
167
168 Ok(())
169 }
170
171 pub fn check_constraints(&self, value: &str) -> Result<(), Error> {
172
173 self.check_length(value.chars().count())?;
174
175 if let Some(ref format) = self.format {
176 match format.as_ref() {
177 ApiStringFormat::Pattern(ref regex) => {
178 if !regex.is_match(value) {
179 bail!("value does not match the regex pattern");
180 }
181 }
182 ApiStringFormat::Enum(ref stringvec) => {
183 if stringvec.iter().find(|&e| *e == value) == None {
184 bail!("value '{}' is not defined in the enumeration.", value);
185 }
186 }
187 ApiStringFormat::Complex(ref subschema) => {
188 parse_property_string(value, subschema)?;
189 }
190 ApiStringFormat::VerifyFn(verify_fn) => {
191 verify_fn(value)?;
192 }
193 }
194 }
195
196 Ok(())
197 }
198
199 }
200
201 #[derive(Debug)]
202 pub struct ArraySchema {
203 pub description: &'static str,
204 pub items: Arc<Schema>,
205 pub min_length: Option<usize>,
206 pub max_length: Option<usize>,
207 }
208
209 impl ArraySchema {
210
211 pub fn new(description: &'static str, item_schema: Arc<Schema>) -> Self {
212 ArraySchema {
213 description: description,
214 items: item_schema,
215 min_length: None,
216 max_length: None,
217 }
218 }
219
220 pub fn min_length(mut self, min_length: usize) -> Self {
221 self.min_length = Some(min_length);
222 self
223 }
224
225 pub fn max_length(mut self, max_length: usize) -> Self {
226 self.max_length = Some(max_length);
227 self
228 }
229
230 fn check_length(&self, length: usize) -> Result<(), Error> {
231
232 if let Some(min_length) = self.min_length {
233 if length < min_length {
234 bail!("array must contain at least {} elements", min_length);
235 }
236 }
237
238 if let Some(max_length) = self.max_length {
239 if length > max_length {
240 bail!("array may only contain {} elements", max_length);
241 }
242 }
243
244 Ok(())
245 }
246 }
247
248 #[derive(Debug)]
249 pub struct ObjectSchema {
250 pub description: &'static str,
251 pub additional_properties: bool,
252 pub properties: HashMap<&'static str, (bool, Arc<Schema>)>,
253 pub default_key: Option<&'static str>,
254 }
255
256 impl ObjectSchema {
257
258 pub fn new(description: &'static str) -> Self {
259 let properties = HashMap::new();
260 ObjectSchema {
261 description: description,
262 additional_properties: false,
263 properties: properties,
264 default_key: None,
265 }
266 }
267
268 pub fn additional_properties(mut self, additional_properties: bool) -> Self {
269 self.additional_properties = additional_properties;
270 self
271 }
272
273 pub fn default_key(mut self, key: &'static str) -> Self {
274 self.default_key = Some(key);
275 self
276 }
277
278 pub fn required<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self {
279 self.properties.insert(name, (false, schema.into()));
280 self
281 }
282
283 pub fn optional<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self {
284 self.properties.insert(name, (true, schema.into()));
285 self
286 }
287 }
288
289 #[derive(Debug)]
290 pub enum Schema {
291 Null,
292 Boolean(BooleanSchema),
293 Integer(IntegerSchema),
294 String(StringSchema),
295 Object(ObjectSchema),
296 Array(ArraySchema),
297 }
298
299 impl From<StringSchema> for Schema {
300 fn from(string_schema: StringSchema) -> Self {
301 Schema::String(string_schema)
302 }
303 }
304
305 impl From<StringSchema> for Arc<Schema> {
306 fn from(string_schema: StringSchema) -> Self {
307 Arc::new(Schema::String(string_schema))
308 }
309 }
310
311 impl From<BooleanSchema> for Schema {
312 fn from(boolean_schema: BooleanSchema) -> Self {
313 Schema::Boolean(boolean_schema)
314 }
315 }
316
317 impl From<BooleanSchema> for Arc<Schema> {
318 fn from(boolean_schema: BooleanSchema) -> Self {
319 Arc::new(Schema::Boolean(boolean_schema))
320 }
321 }
322
323 impl From<IntegerSchema> for Schema {
324 fn from(integer_schema: IntegerSchema) -> Self {
325 Schema::Integer(integer_schema)
326 }
327 }
328
329 impl From<IntegerSchema> for Arc<Schema> {
330 fn from(integer_schema: IntegerSchema) -> Self {
331 Arc::new(Schema::Integer(integer_schema))
332 }
333 }
334
335 impl From<ObjectSchema> for Schema {
336 fn from(object_schema: ObjectSchema) -> Self {
337 Schema::Object(object_schema)
338 }
339 }
340
341 impl From<ObjectSchema> for Arc<Schema> {
342 fn from(object_schema: ObjectSchema) -> Self {
343 Arc::new(Schema::Object(object_schema))
344 }
345 }
346
347 impl From<ArraySchema> for Schema {
348 fn from(array_schema: ArraySchema) -> Self {
349 Schema::Array(array_schema)
350 }
351 }
352
353 impl From<ArraySchema> for Arc<Schema> {
354 fn from(array_schema: ArraySchema) -> Self {
355 Arc::new(Schema::Array(array_schema))
356 }
357 }
358
359 pub enum ApiStringFormat {
360 Enum(Vec<String>),
361 Pattern(&'static Regex),
362 Complex(Arc<Schema>),
363 VerifyFn(fn(&str) -> Result<(), Error>),
364 }
365
366 impl std::fmt::Debug for ApiStringFormat {
367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368 match self {
369 ApiStringFormat::VerifyFn(fnptr) => {
370 write!(f, "VerifyFn({:p}", fnptr)
371 }
372 ApiStringFormat::Enum(strvec) => {
373 write!(f, "Enum({:?}", strvec)
374 }
375 ApiStringFormat::Pattern(regex) => {
376 write!(f, "Pattern({:?}", regex)
377 }
378 ApiStringFormat::Complex(schema) => {
379 write!(f, "Complex({:?}", schema)
380 }
381 }
382 }
383 }
384
385 pub fn parse_boolean(value_str: &str) -> Result<bool, Error> {
386 match value_str.to_lowercase().as_str() {
387 "1" | "on" | "yes" | "true" => Ok(true),
388 "0" | "off" | "no" | "false" => Ok(false),
389 _ => bail!("Unable to parse boolean option."),
390 }
391 }
392
393 fn parse_property_string(value_str: &str, schema: &Schema) -> Result<Value, Error> {
394
395 println!("Parse property string: {}", value_str);
396
397 let mut param_list: Vec<(String, String)> = vec![];
398
399 match schema {
400 Schema::Object(object_schema) => {
401 for key_val in value_str.split(',').filter(|s| !s.is_empty()) {
402 let kv: Vec<&str> = key_val.splitn(2, '=').collect();
403 if kv.len() == 2 {
404 param_list.push((kv[0].into(), kv[1].into()));
405 } else {
406 if let Some(key) = object_schema.default_key {
407 param_list.push((key.into(), kv[0].into()));
408 } else {
409 bail!("Value without key, but schema does not define a default key.");
410 }
411 }
412 }
413
414 return parse_parameter_strings(&param_list, &object_schema, true)
415 .map_err(Error::from);
416
417 }
418 Schema::Array(array_schema) => {
419 let mut array : Vec<Value> = vec![];
420 for value in value_str.split(',').filter(|s| !s.is_empty()) {
421 match parse_simple_value(value, &array_schema.items) {
422 Ok(res) => array.push(res),
423 Err(err) => bail!("unable to parse array element: {}", err),
424 }
425 }
426 array_schema.check_length(array.len())?;
427
428 return Ok(array.into());
429 }
430 _ => {
431 bail!("Got unexpetec schema type.")
432 }
433 }
434
435 }
436
437 pub fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Error> {
438
439 let value = match schema {
440 Schema::Null => {
441 bail!("internal error - found Null schema.");
442 }
443 Schema::Boolean(_boolean_schema) => {
444 let res = parse_boolean(value_str)?;
445 Value::Bool(res)
446 }
447 Schema::Integer(integer_schema) => {
448 let res: isize = value_str.parse()?;
449 integer_schema.check_constraints(res)?;
450 Value::Number(res.into())
451 }
452 Schema::String(string_schema) => {
453 string_schema.check_constraints(value_str)?;
454 Value::String(value_str.into())
455 }
456 _ => bail!("unable to parse complex (sub) objects."),
457 };
458 Ok(value)
459 }
460
461 pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &ObjectSchema, test_required: bool) -> Result<Value, ParameterError> {
462
463 let mut params = json!({});
464
465 let mut errors = ParameterError::new();
466
467 let properties = &schema.properties;
468 let additional_properties = schema.additional_properties;
469
470 for (key, value) in data {
471 if let Some((_optional, prop_schema)) = properties.get::<str>(key) {
472 match prop_schema.as_ref() {
473 Schema::Array(array_schema) => {
474 if params[key] == Value::Null {
475 params[key] = json!([]);
476 }
477 match params[key] {
478 Value::Array(ref mut array) => {
479 match parse_simple_value(value, &array_schema.items) {
480 Ok(res) => array.push(res), // fixme: check_length??
481 Err(err) => errors.push(format_err!("parameter '{}': {}", key, err)),
482 }
483 }
484 _ => errors.push(format_err!("parameter '{}': expected array - type missmatch", key)),
485 }
486 }
487 _ => {
488 match parse_simple_value(value, prop_schema) {
489 Ok(res) => {
490 if params[key] == Value::Null {
491 params[key] = res;
492 } else {
493 errors.push(format_err!("parameter '{}': duplicate parameter.", key));
494 }
495 },
496 Err(err) => errors.push(format_err!("parameter '{}': {}", key, err)),
497 }
498 }
499 }
500 } else {
501 if additional_properties {
502 match params[key] {
503 Value::Null => {
504 params[key] = Value::String(value.to_owned());
505 },
506 Value::String(ref old) => {
507 params[key] = Value::Array(
508 vec![Value::String(old.to_owned()), Value::String(value.to_owned())]);
509 }
510 Value::Array(ref mut array) => {
511 array.push(Value::String(value.to_string()));
512 }
513 _ => errors.push(format_err!("parameter '{}': expected array - type missmatch", key)),
514 }
515 } else {
516 errors.push(format_err!("parameter '{}': schema does not allow additional properties.", key));
517 }
518 }
519 }
520
521 if test_required && errors.len() == 0 {
522 for (name, (optional, _prop_schema)) in properties {
523 if *optional == false && params[name] == Value::Null {
524 errors.push(format_err!("parameter '{}': parameter is missing and it is not optional.", name));
525 }
526 }
527 }
528
529 if errors.len() > 0 {
530 Err(errors)
531 } else {
532 Ok(params)
533 }
534 }
535
536 pub fn parse_query_string(query: &str, schema: &ObjectSchema, test_required: bool) -> Result<Value, ParameterError> {
537
538 let param_list: Vec<(String, String)> =
539 form_urlencoded::parse(query.as_bytes()).into_owned().collect();
540
541 parse_parameter_strings(&param_list, schema, test_required)
542 }
543
544 pub fn verify_json(data: &Value, schema: &Schema) -> Result<(), Error> {
545
546 match schema {
547 Schema::Object(object_schema) => {
548 verify_json_object(data, &object_schema)?;
549 }
550 Schema::Array(array_schema) => {
551 verify_json_array(data, &array_schema)?;
552 }
553 Schema::Null => {
554 if !data.is_null() {
555 bail!("Expected Null, but value is not Null.");
556 }
557 }
558 Schema::Boolean(boolean_schema) => verify_json_boolean(data, &boolean_schema)?,
559 Schema::Integer(integer_schema) => verify_json_integer(data, &integer_schema)?,
560 Schema::String(string_schema) => verify_json_string(data, &string_schema)?,
561 }
562 Ok(())
563 }
564
565 pub fn verify_json_string(data: &Value, schema: &StringSchema) -> Result<(), Error> {
566 if let Some(value) = data.as_str() {
567 schema.check_constraints(value)
568 } else {
569 bail!("Expected string value.");
570 }
571 }
572
573 pub fn verify_json_boolean(data: &Value, _schema: &BooleanSchema) -> Result<(), Error> {
574 if !data.is_boolean() {
575 bail!("Expected boolean value.");
576 }
577 Ok(())
578 }
579
580 pub fn verify_json_integer(data: &Value, schema: &IntegerSchema) -> Result<(), Error> {
581 if let Some(value) = data.as_i64() {
582 schema.check_constraints(value as isize)
583 } else {
584 bail!("Expected integer value.");
585 }
586 }
587
588 pub fn verify_json_array(data: &Value, schema: &ArraySchema) -> Result<(), Error> {
589
590 let list = match data {
591 Value::Array(ref list) => list,
592 Value::Object(_) => bail!("Expected array - got object."),
593 _ => bail!("Expected array - got scalar value."),
594 };
595
596 schema.check_length(list.len())?;
597
598 for item in list {
599 verify_json(item, &schema.items)?;
600 }
601
602 Ok(())
603 }
604
605 pub fn verify_json_object(data: &Value, schema: &ObjectSchema) -> Result<(), Error> {
606
607 let map = match data {
608 Value::Object(ref map) => map,
609 Value::Array(_) => bail!("Expected object - got array."),
610 _ => bail!("Expected object - got scalar value."),
611 };
612
613 let properties = &schema.properties;
614 let additional_properties = schema.additional_properties;
615
616 for (key, value) in map {
617 if let Some((_optional, prop_schema)) = properties.get::<str>(key) {
618 match prop_schema.as_ref() {
619 Schema::Object(object_schema) => {
620 verify_json_object(value, object_schema)?;
621 }
622 Schema::Array(array_schema) => {
623 verify_json_array(value, array_schema)?;
624 }
625 _ => verify_json(value, prop_schema)?,
626 }
627 } else {
628 if !additional_properties {
629 bail!("property '{}': schema does not allow additional properties.", key);
630 }
631 }
632 }
633
634 for (name, (optional, _prop_schema)) in properties {
635 if *optional == false && data[name] == Value::Null {
636 bail!("property '{}': property is missing and it is not optional.", name);
637 }
638 }
639
640 Ok(())
641 }
642
643 #[test]
644 fn test_schema1() {
645 let schema = Schema::Object(ObjectSchema {
646 description: "TEST",
647 additional_properties: false,
648 properties: {
649 let map = HashMap::new();
650
651 map
652 },
653 default_key: None,
654 });
655
656 println!("TEST Schema: {:?}", schema);
657 }
658
659 #[test]
660 fn test_query_string() {
661
662 let schema = ObjectSchema::new("Parameters.")
663 .required("name", StringSchema::new("Name."));
664
665 let res = parse_query_string("", &schema, true);
666 assert!(res.is_err());
667
668 let schema = ObjectSchema::new("Parameters.")
669 .optional("name", StringSchema::new("Name."));
670
671 let res = parse_query_string("", &schema, true);
672 assert!(res.is_ok());
673
674 // TEST min_length and max_length
675
676 let schema = ObjectSchema::new("Parameters.")
677 .required(
678 "name", StringSchema::new("Name.")
679 .min_length(5)
680 .max_length(10)
681 );
682
683 let res = parse_query_string("name=abcd", &schema, true);
684 assert!(res.is_err());
685
686 let res = parse_query_string("name=abcde", &schema, true);
687 assert!(res.is_ok());
688
689 let res = parse_query_string("name=abcdefghijk", &schema, true);
690 assert!(res.is_err());
691
692 let res = parse_query_string("name=abcdefghij", &schema, true);
693 assert!(res.is_ok());
694
695 // TEST regex pattern
696
697 use lazy_static::lazy_static;
698 lazy_static! {
699 static ref TEST_REGEX: Regex = Regex::new("test").unwrap();
700 static ref TEST2_REGEX: Regex = Regex::new("^test$").unwrap();
701 }
702
703 let schema = ObjectSchema::new("Parameters.")
704 .required(
705 "name", StringSchema::new("Name.")
706 .format(Arc::new(ApiStringFormat::Pattern(&TEST_REGEX)))
707 );
708
709 let res = parse_query_string("name=abcd", &schema, true);
710 assert!(res.is_err());
711
712 let res = parse_query_string("name=ateststring", &schema, true);
713 assert!(res.is_ok());
714
715 let schema = ObjectSchema::new("Parameters.")
716 .required(
717 "name", StringSchema::new("Name.")
718 .format(Arc::new(ApiStringFormat::Pattern(&TEST2_REGEX)))
719 );
720
721 let res = parse_query_string("name=ateststring", &schema, true);
722 assert!(res.is_err());
723
724 let res = parse_query_string("name=test", &schema, true);
725 assert!(res.is_ok());
726
727 // TEST string enums
728
729 let schema = ObjectSchema::new("Parameters.")
730 .required(
731 "name", StringSchema::new("Name.")
732 .format(Arc::new(ApiStringFormat::Enum(vec!["ev1".into(), "ev2".into()])))
733 );
734
735 let res = parse_query_string("name=noenum", &schema, true);
736 assert!(res.is_err());
737
738 let res = parse_query_string("name=ev1", &schema, true);
739 assert!(res.is_ok());
740
741 let res = parse_query_string("name=ev2", &schema, true);
742 assert!(res.is_ok());
743
744 let res = parse_query_string("name=ev3", &schema, true);
745 assert!(res.is_err());
746
747 }
748
749 #[test]
750 fn test_query_integer() {
751
752 let schema = ObjectSchema::new("Parameters.")
753 .required(
754 "count" , IntegerSchema::new("Count.")
755 );
756
757 let res = parse_query_string("", &schema, true);
758 assert!(res.is_err());
759
760 let schema = ObjectSchema::new("Parameters.")
761 .optional(
762 "count", IntegerSchema::new("Count.")
763 .minimum(-3)
764 .maximum(50)
765 );
766
767 let res = parse_query_string("", &schema, true);
768 assert!(res.is_ok());
769
770 let res = parse_query_string("count=abc", &schema, false);
771 assert!(res.is_err());
772
773 let res = parse_query_string("count=30", &schema, false);
774 assert!(res.is_ok());
775
776 let res = parse_query_string("count=-1", &schema, false);
777 assert!(res.is_ok());
778
779 let res = parse_query_string("count=300", &schema, false);
780 assert!(res.is_err());
781
782 let res = parse_query_string("count=-30", &schema, false);
783 assert!(res.is_err());
784
785 let res = parse_query_string("count=50", &schema, false);
786 assert!(res.is_ok());
787
788 let res = parse_query_string("count=-3", &schema, false);
789 assert!(res.is_ok());
790 }
791
792 #[test]
793 fn test_query_boolean() {
794
795 let schema = ObjectSchema::new("Parameters.")
796 .required(
797 "force", BooleanSchema::new("Force.")
798 );
799
800 let res = parse_query_string("", &schema, true);
801 assert!(res.is_err());
802
803 let schema = ObjectSchema::new("Parameters.")
804 .optional(
805 "force", BooleanSchema::new("Force.")
806 );
807
808 let res = parse_query_string("", &schema, true);
809 assert!(res.is_ok());
810
811 let res = parse_query_string("a=b", &schema, true);
812 assert!(res.is_err());
813
814
815 let res = parse_query_string("force", &schema, true);
816 assert!(res.is_err());
817
818 let res = parse_query_string("force=yes", &schema, true);
819 assert!(res.is_ok());
820 let res = parse_query_string("force=1", &schema, true);
821 assert!(res.is_ok());
822 let res = parse_query_string("force=On", &schema, true);
823 assert!(res.is_ok());
824 let res = parse_query_string("force=TRUE", &schema, true);
825 assert!(res.is_ok());
826 let res = parse_query_string("force=TREU", &schema, true);
827 assert!(res.is_err());
828
829 let res = parse_query_string("force=NO", &schema, true);
830 assert!(res.is_ok());
831 let res = parse_query_string("force=0", &schema, true);
832 assert!(res.is_ok());
833 let res = parse_query_string("force=off", &schema, true);
834 assert!(res.is_ok());
835 let res = parse_query_string("force=False", &schema, true);
836 assert!(res.is_ok());
837 }
838
839 #[test]
840 fn test_verify_function() {
841
842 let schema = ObjectSchema::new("Parameters.")
843 .required(
844 "p1", StringSchema::new("P1")
845 .format(ApiStringFormat::VerifyFn(|value| {
846 if value == "test" { return Ok(()) };
847 bail!("format error");
848 }).into())
849 );
850
851 let res = parse_query_string("p1=tes", &schema, true);
852 assert!(res.is_err());
853 let res = parse_query_string("p1=test", &schema, true);
854 assert!(res.is_ok());
855 }
856
857 #[test]
858 fn test_verify_complex_object() {
859
860 let nic_models = Arc::new(ApiStringFormat::Enum(
861 vec!["e1000".into(), "virtio".into()]));
862
863 let param_schema: Arc<Schema> = ObjectSchema::new("Properties.")
864 .default_key("model")
865 .required("model", StringSchema::new("Ethernet device Model.")
866 .format(nic_models))
867 .optional("enable", BooleanSchema::new("Enable device."))
868 .into();
869
870 let schema = ObjectSchema::new("Parameters.")
871 .required(
872 "net0", StringSchema::new("First Network device.")
873 .format(ApiStringFormat::Complex(param_schema).into())
874 );
875
876 let res = parse_query_string("", &schema, true);
877 assert!(res.is_err());
878
879 let res = parse_query_string("test=abc", &schema, true);
880 assert!(res.is_err());
881
882 let res = parse_query_string("net0=model=abc", &schema, true);
883 assert!(res.is_err());
884
885 let res = parse_query_string("net0=model=virtio", &schema, true);
886 assert!(res.is_ok());
887
888 let res = parse_query_string("net0=model=virtio,enable=1", &schema, true);
889 assert!(res.is_ok());
890
891 let res = parse_query_string("net0=virtio,enable=no", &schema, true);
892 assert!(res.is_ok());
893 }
894
895 #[test]
896 fn test_verify_complex_array() {
897
898 let param_schema: Arc<Schema> = ArraySchema::new(
899 "Integer List.", Arc::new(IntegerSchema::new("Soemething").into()))
900 .into();
901
902 let schema = ObjectSchema::new("Parameters.")
903 .required(
904 "list", StringSchema::new("A list on integers, comma separated.")
905 .format(ApiStringFormat::Complex(param_schema).into())
906 );
907
908 let res = parse_query_string("", &schema, true);
909 assert!(res.is_err());
910
911 let res = parse_query_string("list=", &schema, true);
912 assert!(res.is_ok());
913
914 let res = parse_query_string("list=abc", &schema, true);
915 assert!(res.is_err());
916
917 let res = parse_query_string("list=1", &schema, true);
918 assert!(res.is_ok());
919
920 let res = parse_query_string("list=2,3,4,5", &schema, true);
921 assert!(res.is_ok());
922
923 let param_schema: Arc<Schema> = ArraySchema::new(
924 "Integer List.", Arc::new(IntegerSchema::new("Soemething").into()))
925 .min_length(1)
926 .max_length(3)
927 .into();
928
929 let schema = ObjectSchema::new("Parameters.")
930 .required(
931 "list", StringSchema::new("A list on integers, comma separated.")
932 .format(ApiStringFormat::Complex(param_schema).into())
933 );
934
935 let res = parse_query_string("list=", &schema, true);
936 assert!(res.is_err());
937
938 let res = parse_query_string("list=1,2,3", &schema, true);
939 assert!(res.is_ok());
940
941 let res = parse_query_string("list=2,3,4,5", &schema, true);
942 assert!(res.is_err());
943 }