]>
git.proxmox.com Git - proxmox-backup.git/blob - src/api/schema.rs
2 use std
::collections
::HashMap
;
3 use serde_json
::{json, Value}
;
4 use url
::form_urlencoded
;
10 pub struct ParameterError
{
11 error_list
: Vec
<Error
>,
16 pub fn new() -> Self {
17 Self { error_list: vec![] }
20 pub fn push(&mut self, value
: Error
) {
21 self.error_list
.push(value
);
24 pub fn len(&self) -> usize {
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"
40 pub struct BooleanSchema
{
41 pub description
: &'
static str,
42 pub default: Option
<bool
>,
47 pub fn new(description
: &'
static str) -> Self {
49 description
: description
,
54 pub fn default(mut self, default: bool
) -> Self {
55 self.default = Some(default);
61 pub struct IntegerSchema
{
62 pub description
: &'
static str,
63 pub minimum
: Option
<isize>,
64 pub maximum
: Option
<isize>,
65 pub default: Option
<isize>,
70 pub fn new(description
: &'
static str) -> Self {
72 description
: description
,
79 pub fn default(mut self, default: isize) -> Self {
80 self.default = Some(default);
84 pub fn minimum(mut self, minimum
: isize) -> Self {
85 self.minimum
= Some(minimum
);
89 pub fn maximum(mut self, maximium
: isize) -> Self {
90 self.maximum
= Some(maximium
);
94 fn check_constraints(&self, value
: isize) -> Result
<(), Error
> {
96 if let Some(minimum
) = self.minimum
{
98 bail
!("value must have a minimum value of {}", minimum
);
102 if let Some(maximum
) = self.maximum
{
104 bail
!("value must have a maximum value of {}", maximum
);
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
>>,
124 pub fn new(description
: &'
static str) -> Self {
126 description
: description
,
134 pub fn default(mut self, text
: &'
static str) -> Self {
135 self.default = Some(text
);
139 pub fn format(mut self, format
: Arc
<ApiStringFormat
>) -> Self {
140 self.format
= Some(format
);
144 pub fn min_length(mut self, min_length
: usize) -> Self {
145 self.min_length
= Some(min_length
);
149 pub fn max_length(mut self, max_length
: usize) -> Self {
150 self.max_length
= Some(max_length
);
154 fn check_length(&self, length
: usize) -> Result
<(), Error
> {
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
);
162 if let Some(max_length
) = self.max_length
{
163 if length
> max_length
{
164 bail
!("value may only be {} characters long", max_length
);
171 pub fn check_constraints(&self, value
: &str) -> Result
<(), Error
> {
173 self.check_length(value
.chars().count())?
;
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");
182 ApiStringFormat
::Enum(ref stringvec
) => {
183 if stringvec
.iter().find(|&e
| *e
== value
) == None
{
184 bail
!("value '{}' is not defined in the enumeration.", value
);
187 ApiStringFormat
::Complex(ref subschema
) => {
188 parse_property_string(value
, subschema
)?
;
190 ApiStringFormat
::VerifyFn(verify_fn
) => {
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>,
211 pub fn new(description
: &'
static str, item_schema
: Arc
<Schema
>) -> Self {
213 description
: description
,
220 pub fn min_length(mut self, min_length
: usize) -> Self {
221 self.min_length
= Some(min_length
);
225 pub fn max_length(mut self, max_length
: usize) -> Self {
226 self.max_length
= Some(max_length
);
230 fn check_length(&self, length
: usize) -> Result
<(), Error
> {
232 if let Some(min_length
) = self.min_length
{
233 if length
< min_length
{
234 bail
!("array must contain at least {} elements", min_length
);
238 if let Some(max_length
) = self.max_length
{
239 if length
> max_length
{
240 bail
!("array may only contain {} elements", max_length
);
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>,
258 pub fn new(description
: &'
static str) -> Self {
259 let properties
= HashMap
::new();
261 description
: description
,
262 additional_properties
: false,
263 properties
: properties
,
268 pub fn additional_properties(mut self, additional_properties
: bool
) -> Self {
269 self.additional_properties
= additional_properties
;
273 pub fn default_key(mut self, key
: &'
static str) -> Self {
274 self.default_key
= Some(key
);
278 pub fn required
<S
: Into
<Arc
<Schema
>>>(mut self, name
: &'
static str, schema
: S
) -> Self {
279 self.properties
.insert(name
, (false, schema
.into()));
283 pub fn optional
<S
: Into
<Arc
<Schema
>>>(mut self, name
: &'
static str, schema
: S
) -> Self {
284 self.properties
.insert(name
, (true, schema
.into()));
292 Boolean(BooleanSchema
),
293 Integer(IntegerSchema
),
294 String(StringSchema
),
295 Object(ObjectSchema
),
299 impl From
<StringSchema
> for Schema
{
300 fn from(string_schema
: StringSchema
) -> Self {
301 Schema
::String(string_schema
)
305 impl From
<StringSchema
> for Arc
<Schema
> {
306 fn from(string_schema
: StringSchema
) -> Self {
307 Arc
::new(Schema
::String(string_schema
))
311 impl From
<BooleanSchema
> for Schema
{
312 fn from(boolean_schema
: BooleanSchema
) -> Self {
313 Schema
::Boolean(boolean_schema
)
317 impl From
<BooleanSchema
> for Arc
<Schema
> {
318 fn from(boolean_schema
: BooleanSchema
) -> Self {
319 Arc
::new(Schema
::Boolean(boolean_schema
))
323 impl From
<IntegerSchema
> for Schema
{
324 fn from(integer_schema
: IntegerSchema
) -> Self {
325 Schema
::Integer(integer_schema
)
329 impl From
<IntegerSchema
> for Arc
<Schema
> {
330 fn from(integer_schema
: IntegerSchema
) -> Self {
331 Arc
::new(Schema
::Integer(integer_schema
))
335 impl From
<ObjectSchema
> for Schema
{
336 fn from(object_schema
: ObjectSchema
) -> Self {
337 Schema
::Object(object_schema
)
341 impl From
<ObjectSchema
> for Arc
<Schema
> {
342 fn from(object_schema
: ObjectSchema
) -> Self {
343 Arc
::new(Schema
::Object(object_schema
))
347 impl From
<ArraySchema
> for Schema
{
348 fn from(array_schema
: ArraySchema
) -> Self {
349 Schema
::Array(array_schema
)
353 impl From
<ArraySchema
> for Arc
<Schema
> {
354 fn from(array_schema
: ArraySchema
) -> Self {
355 Arc
::new(Schema
::Array(array_schema
))
359 pub enum ApiStringFormat
{
361 Pattern(&'
static Regex
),
362 Complex(Arc
<Schema
>),
363 VerifyFn(fn(&str) -> Result
<(), Error
>),
366 impl std
::fmt
::Debug
for ApiStringFormat
{
367 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
369 ApiStringFormat
::VerifyFn(fnptr
) => {
370 write
!(f
, "VerifyFn({:p}", fnptr
)
372 ApiStringFormat
::Enum(strvec
) => {
373 write
!(f
, "Enum({:?}", strvec
)
375 ApiStringFormat
::Pattern(regex
) => {
376 write
!(f
, "Pattern({:?}", regex
)
378 ApiStringFormat
::Complex(schema
) => {
379 write
!(f
, "Complex({:?}", schema
)
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."),
393 fn parse_property_string(value_str
: &str, schema
: &Schema
) -> Result
<Value
, Error
> {
395 println
!("Parse property string: {}", value_str
);
397 let mut param_list
: Vec
<(String
, String
)> = vec
![];
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();
404 param_list
.push((kv
[0].into(), kv
[1].into()));
406 if let Some(key
) = object_schema
.default_key
{
407 param_list
.push((key
.into(), kv
[0].into()));
409 bail
!("Value without key, but schema does not define a default key.");
414 return parse_parameter_strings(¶m_list
, &object_schema
, true)
415 .map_err(Error
::from
);
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
),
426 array_schema
.check_length(array
.len())?
;
428 return Ok(array
.into());
431 bail
!("Got unexpetec schema type.")
437 pub fn parse_simple_value(value_str
: &str, schema
: &Schema
) -> Result
<Value
, Error
> {
439 let value
= match schema
{
441 bail
!("internal error - found Null schema.");
443 Schema
::Boolean(_boolean_schema
) => {
444 let res
= parse_boolean(value_str
)?
;
447 Schema
::Integer(integer_schema
) => {
448 let res
: isize = value_str
.parse()?
;
449 integer_schema
.check_constraints(res
)?
;
450 Value
::Number(res
.into())
452 Schema
::String(string_schema
) => {
453 string_schema
.check_constraints(value_str
)?
;
454 Value
::String(value_str
.into())
456 _
=> bail
!("unable to parse complex (sub) objects."),
461 pub fn parse_parameter_strings(data
: &Vec
<(String
, String
)>, schema
: &ObjectSchema
, test_required
: bool
) -> Result
<Value
, ParameterError
> {
463 let mut params
= json
!({}
);
465 let mut errors
= ParameterError
::new();
467 let properties
= &schema
.properties
;
468 let additional_properties
= schema
.additional_properties
;
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
!([]);
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
)),
484 _
=> errors
.push(format_err
!("parameter '{}': expected array - type missmatch", key
)),
488 match parse_simple_value(value
, prop_schema
) {
490 if params
[key
] == Value
::Null
{
493 errors
.push(format_err
!("parameter '{}': duplicate parameter.", key
));
496 Err(err
) => errors
.push(format_err
!("parameter '{}': {}", key
, err
)),
501 if additional_properties
{
504 params
[key
] = Value
::String(value
.to_owned());
506 Value
::String(ref old
) => {
507 params
[key
] = Value
::Array(
508 vec
![Value
::String(old
.to_owned()), Value
::String(value
.to_owned())]);
510 Value
::Array(ref mut array
) => {
511 array
.push(Value
::String(value
.to_string()));
513 _
=> errors
.push(format_err
!("parameter '{}': expected array - type missmatch", key
)),
516 errors
.push(format_err
!("parameter '{}': schema does not allow additional properties.", key
));
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
));
529 if errors
.len() > 0 {
536 pub fn parse_query_string(query
: &str, schema
: &ObjectSchema
, test_required
: bool
) -> Result
<Value
, ParameterError
> {
538 let param_list
: Vec
<(String
, String
)> =
539 form_urlencoded
::parse(query
.as_bytes()).into_owned().collect();
541 parse_parameter_strings(¶m_list
, schema
, test_required
)
544 pub fn verify_json(data
: &Value
, schema
: &Schema
) -> Result
<(), Error
> {
547 Schema
::Object(object_schema
) => {
548 verify_json_object(data
, &object_schema
)?
;
550 Schema
::Array(array_schema
) => {
551 verify_json_array(data
, &array_schema
)?
;
555 bail
!("Expected Null, but value is not Null.");
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
)?
,
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
)
569 bail
!("Expected string value.");
573 pub fn verify_json_boolean(data
: &Value
, _schema
: &BooleanSchema
) -> Result
<(), Error
> {
574 if !data
.is_boolean() {
575 bail
!("Expected boolean value.");
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)
584 bail
!("Expected integer value.");
588 pub fn verify_json_array(data
: &Value
, schema
: &ArraySchema
) -> Result
<(), Error
> {
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."),
596 schema
.check_length(list
.len())?
;
599 verify_json(item
, &schema
.items
)?
;
605 pub fn verify_json_object(data
: &Value
, schema
: &ObjectSchema
) -> Result
<(), Error
> {
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."),
613 let properties
= &schema
.properties
;
614 let additional_properties
= schema
.additional_properties
;
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
)?
;
622 Schema
::Array(array_schema
) => {
623 verify_json_array(value
, array_schema
)?
;
625 _
=> verify_json(value
, prop_schema
)?
,
628 if !additional_properties
{
629 bail
!("property '{}': schema does not allow additional properties.", key
);
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
);
645 let schema
= Schema
::Object(ObjectSchema
{
647 additional_properties
: false,
649 let map
= HashMap
::new();
656 println
!("TEST Schema: {:?}", schema
);
660 fn test_query_string() {
662 let schema
= ObjectSchema
::new("Parameters.")
663 .required("name", StringSchema
::new("Name."));
665 let res
= parse_query_string("", &schema
, true);
666 assert
!(res
.is_err());
668 let schema
= ObjectSchema
::new("Parameters.")
669 .optional("name", StringSchema
::new("Name."));
671 let res
= parse_query_string("", &schema
, true);
672 assert
!(res
.is_ok());
674 // TEST min_length and max_length
676 let schema
= ObjectSchema
::new("Parameters.")
678 "name", StringSchema
::new("Name.")
683 let res
= parse_query_string("name=abcd", &schema
, true);
684 assert
!(res
.is_err());
686 let res
= parse_query_string("name=abcde", &schema
, true);
687 assert
!(res
.is_ok());
689 let res
= parse_query_string("name=abcdefghijk", &schema
, true);
690 assert
!(res
.is_err());
692 let res
= parse_query_string("name=abcdefghij", &schema
, true);
693 assert
!(res
.is_ok());
695 // TEST regex pattern
697 use lazy_static
::lazy_static
;
699 static ref TEST_REGEX
: Regex
= Regex
::new("test").unwrap();
700 static ref TEST2_REGEX
: Regex
= Regex
::new("^test$").unwrap();
703 let schema
= ObjectSchema
::new("Parameters.")
705 "name", StringSchema
::new("Name.")
706 .format(Arc
::new(ApiStringFormat
::Pattern(&TEST_REGEX
)))
709 let res
= parse_query_string("name=abcd", &schema
, true);
710 assert
!(res
.is_err());
712 let res
= parse_query_string("name=ateststring", &schema
, true);
713 assert
!(res
.is_ok());
715 let schema
= ObjectSchema
::new("Parameters.")
717 "name", StringSchema
::new("Name.")
718 .format(Arc
::new(ApiStringFormat
::Pattern(&TEST2_REGEX
)))
721 let res
= parse_query_string("name=ateststring", &schema
, true);
722 assert
!(res
.is_err());
724 let res
= parse_query_string("name=test", &schema
, true);
725 assert
!(res
.is_ok());
729 let schema
= ObjectSchema
::new("Parameters.")
731 "name", StringSchema
::new("Name.")
732 .format(Arc
::new(ApiStringFormat
::Enum(vec
!["ev1".into(), "ev2".into()])))
735 let res
= parse_query_string("name=noenum", &schema
, true);
736 assert
!(res
.is_err());
738 let res
= parse_query_string("name=ev1", &schema
, true);
739 assert
!(res
.is_ok());
741 let res
= parse_query_string("name=ev2", &schema
, true);
742 assert
!(res
.is_ok());
744 let res
= parse_query_string("name=ev3", &schema
, true);
745 assert
!(res
.is_err());
750 fn test_query_integer() {
752 let schema
= ObjectSchema
::new("Parameters.")
754 "count" , IntegerSchema
::new("Count.")
757 let res
= parse_query_string("", &schema
, true);
758 assert
!(res
.is_err());
760 let schema
= ObjectSchema
::new("Parameters.")
762 "count", IntegerSchema
::new("Count.")
767 let res
= parse_query_string("", &schema
, true);
768 assert
!(res
.is_ok());
770 let res
= parse_query_string("count=abc", &schema
, false);
771 assert
!(res
.is_err());
773 let res
= parse_query_string("count=30", &schema
, false);
774 assert
!(res
.is_ok());
776 let res
= parse_query_string("count=-1", &schema
, false);
777 assert
!(res
.is_ok());
779 let res
= parse_query_string("count=300", &schema
, false);
780 assert
!(res
.is_err());
782 let res
= parse_query_string("count=-30", &schema
, false);
783 assert
!(res
.is_err());
785 let res
= parse_query_string("count=50", &schema
, false);
786 assert
!(res
.is_ok());
788 let res
= parse_query_string("count=-3", &schema
, false);
789 assert
!(res
.is_ok());
793 fn test_query_boolean() {
795 let schema
= ObjectSchema
::new("Parameters.")
797 "force", BooleanSchema
::new("Force.")
800 let res
= parse_query_string("", &schema
, true);
801 assert
!(res
.is_err());
803 let schema
= ObjectSchema
::new("Parameters.")
805 "force", BooleanSchema
::new("Force.")
808 let res
= parse_query_string("", &schema
, true);
809 assert
!(res
.is_ok());
811 let res
= parse_query_string("a=b", &schema
, true);
812 assert
!(res
.is_err());
815 let res
= parse_query_string("force", &schema
, true);
816 assert
!(res
.is_err());
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());
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());
840 fn test_verify_function() {
842 let schema
= ObjectSchema
::new("Parameters.")
844 "p1", StringSchema
::new("P1")
845 .format(ApiStringFormat
::VerifyFn(|value
| {
846 if value
== "test" { return Ok(()) }
;
847 bail
!("format error");
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());
858 fn test_verify_complex_object() {
860 let nic_models
= Arc
::new(ApiStringFormat
::Enum(
861 vec
!["e1000".into(), "virtio".into()]));
863 let param_schema
: Arc
<Schema
> = ObjectSchema
::new("Properties.")
864 .default_key("model")
865 .required("model", StringSchema
::new("Ethernet device Model.")
867 .optional("enable", BooleanSchema
::new("Enable device."))
870 let schema
= ObjectSchema
::new("Parameters.")
872 "net0", StringSchema
::new("First Network device.")
873 .format(ApiStringFormat
::Complex(param_schema
).into())
876 let res
= parse_query_string("", &schema
, true);
877 assert
!(res
.is_err());
879 let res
= parse_query_string("test=abc", &schema
, true);
880 assert
!(res
.is_err());
882 let res
= parse_query_string("net0=model=abc", &schema
, true);
883 assert
!(res
.is_err());
885 let res
= parse_query_string("net0=model=virtio", &schema
, true);
886 assert
!(res
.is_ok());
888 let res
= parse_query_string("net0=model=virtio,enable=1", &schema
, true);
889 assert
!(res
.is_ok());
891 let res
= parse_query_string("net0=virtio,enable=no", &schema
, true);
892 assert
!(res
.is_ok());
896 fn test_verify_complex_array() {
898 let param_schema
: Arc
<Schema
> = ArraySchema
::new(
899 "Integer List.", Arc
::new(IntegerSchema
::new("Soemething").into()))
902 let schema
= ObjectSchema
::new("Parameters.")
904 "list", StringSchema
::new("A list on integers, comma separated.")
905 .format(ApiStringFormat
::Complex(param_schema
).into())
908 let res
= parse_query_string("", &schema
, true);
909 assert
!(res
.is_err());
911 let res
= parse_query_string("list=", &schema
, true);
912 assert
!(res
.is_ok());
914 let res
= parse_query_string("list=abc", &schema
, true);
915 assert
!(res
.is_err());
917 let res
= parse_query_string("list=1", &schema
, true);
918 assert
!(res
.is_ok());
920 let res
= parse_query_string("list=2,3,4,5", &schema
, true);
921 assert
!(res
.is_ok());
923 let param_schema
: Arc
<Schema
> = ArraySchema
::new(
924 "Integer List.", Arc
::new(IntegerSchema
::new("Soemething").into()))
929 let schema
= ObjectSchema
::new("Parameters.")
931 "list", StringSchema
::new("A list on integers, comma separated.")
932 .format(ApiStringFormat
::Complex(param_schema
).into())
935 let res
= parse_query_string("list=", &schema
, true);
936 assert
!(res
.is_err());
938 let res
= parse_query_string("list=1,2,3", &schema
, true);
939 assert
!(res
.is_ok());
941 let res
= parse_query_string("list=2,3,4,5", &schema
, true);
942 assert
!(res
.is_err());