schema: implement verify_json
This commit is contained in:
		@ -92,6 +92,23 @@ impl IntegerSchema {
 | 
				
			|||||||
        self.maximum = Some(maximium);
 | 
					        self.maximum = Some(maximium);
 | 
				
			||||||
        self
 | 
					        self
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn check_constraints(&self, value: isize) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(minimum) = self.minimum {
 | 
				
			||||||
 | 
					            if value < minimum {
 | 
				
			||||||
 | 
					                bail!("value must have a minimum value of {}", minimum);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(maximum) = self.maximum {
 | 
				
			||||||
 | 
					            if value > maximum {
 | 
				
			||||||
 | 
					                bail!("value must have a maximum value of {}", maximum);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -153,6 +170,34 @@ impl StringSchema {
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn check_constraints(&self, value: &str) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.check_length(value.chars().count())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(ref format) = self.format {
 | 
				
			||||||
 | 
					            match format.as_ref() {
 | 
				
			||||||
 | 
					                ApiStringFormat::Pattern(ref regex) => {
 | 
				
			||||||
 | 
					                    if !regex.is_match(value) {
 | 
				
			||||||
 | 
					                        bail!("value does not match the regex pattern");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ApiStringFormat::Enum(ref stringvec) => {
 | 
				
			||||||
 | 
					                    if stringvec.iter().find(|&e| *e == value) == None {
 | 
				
			||||||
 | 
					                        bail!("value is not defined in the enumeration.");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ApiStringFormat::Complex(ref subschema) => {
 | 
				
			||||||
 | 
					                    parse_property_string(value, subschema)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ApiStringFormat::VerifyFn(verify_fn) => {
 | 
				
			||||||
 | 
					                    verify_fn(value)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
@ -403,48 +448,12 @@ pub fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Err
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        Schema::Integer(integer_schema) => {
 | 
					        Schema::Integer(integer_schema) => {
 | 
				
			||||||
            let res: isize = value_str.parse()?;
 | 
					            let res: isize = value_str.parse()?;
 | 
				
			||||||
 | 
					            integer_schema.check_constraints(res)?;
 | 
				
			||||||
            if let Some(minimum) = integer_schema.minimum {
 | 
					 | 
				
			||||||
                if res < minimum {
 | 
					 | 
				
			||||||
                    bail!("value must have a minimum value of {}", minimum);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if let Some(maximum) = integer_schema.maximum {
 | 
					 | 
				
			||||||
                if res > maximum {
 | 
					 | 
				
			||||||
                    bail!("value must have a maximum value of {}", maximum);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Value::Number(res.into())
 | 
					            Value::Number(res.into())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Schema::String(string_schema) => {
 | 
					        Schema::String(string_schema) => {
 | 
				
			||||||
            let res: String = value_str.into();
 | 
					            string_schema.check_constraints(value_str)?;
 | 
				
			||||||
 | 
					            Value::String(value_str.into())
 | 
				
			||||||
            string_schema.check_length(res.chars().count())?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if let Some(ref format) = string_schema.format {
 | 
					 | 
				
			||||||
                match format.as_ref() {
 | 
					 | 
				
			||||||
                    ApiStringFormat::Pattern(ref regex) => {
 | 
					 | 
				
			||||||
                        if !regex.is_match(&res) {
 | 
					 | 
				
			||||||
                            bail!("value does not match the regex pattern");
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    ApiStringFormat::Enum(ref stringvec) => {
 | 
					 | 
				
			||||||
                        if stringvec.iter().find(|&e| *e == res) == None {
 | 
					 | 
				
			||||||
                            bail!("value is not defined in the enumeration.");
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    ApiStringFormat::Complex(ref subschema) => {
 | 
					 | 
				
			||||||
                        parse_property_string(&res, subschema)?;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    ApiStringFormat::VerifyFn(verify_fn) => {
 | 
					 | 
				
			||||||
                        verify_fn(&res)?;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Value::String(res)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        _ => bail!("unable to parse complex (sub) objects."),
 | 
					        _ => bail!("unable to parse complex (sub) objects."),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -536,6 +545,106 @@ pub fn parse_query_string(query: &str, schema: &ObjectSchema, test_required: boo
 | 
				
			|||||||
    parse_parameter_strings(¶m_list, schema, test_required)
 | 
					    parse_parameter_strings(¶m_list, schema, test_required)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json(data: &Value, schema: &Schema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match schema {
 | 
				
			||||||
 | 
					        Schema::Object(object_schema) => {
 | 
				
			||||||
 | 
					            verify_json_object(data, &object_schema)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Schema::Array(array_schema) => {
 | 
				
			||||||
 | 
					            verify_json_array(data, &array_schema)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Schema::Null => {
 | 
				
			||||||
 | 
					            if !data.is_null() {
 | 
				
			||||||
 | 
					                bail!("Expected Null, but value is not Null.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Schema::Boolean(boolean_schema) => verify_json_boolean(data, &boolean_schema)?,
 | 
				
			||||||
 | 
					        Schema::Integer(integer_schema) => verify_json_integer(data, &integer_schema)?,
 | 
				
			||||||
 | 
					        Schema::String(string_schema) => verify_json_string(data, &string_schema)?,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json_string(data: &Value, schema: &StringSchema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					    if let Some(value) = data.as_str() {
 | 
				
			||||||
 | 
					        schema.check_constraints(value)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        bail!("Expected string value.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json_boolean(data: &Value, schema: &BooleanSchema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					    if !data.is_boolean() {
 | 
				
			||||||
 | 
					        bail!("Expected boolean value.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json_integer(data: &Value, schema: &IntegerSchema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Some(value) = data.as_i64() {
 | 
				
			||||||
 | 
					        schema.check_constraints(value as isize)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        bail!("Expected integer value.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json_array(data: &Value, schema: &ArraySchema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let list = match data {
 | 
				
			||||||
 | 
					        Value::Array(ref list) => list,
 | 
				
			||||||
 | 
					        Value::Object(_) => bail!("Expected array - got object."),
 | 
				
			||||||
 | 
					        _ => bail!("Expected array - got scalar value."),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    schema.check_length(list.len())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for item in list {
 | 
				
			||||||
 | 
					        verify_json(item, &schema.items)?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn verify_json_object(data: &Value, schema: &ObjectSchema) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let map = match data {
 | 
				
			||||||
 | 
					        Value::Object(ref map) => map,
 | 
				
			||||||
 | 
					        Value::Array(_) => bail!("Expected object - got array."),
 | 
				
			||||||
 | 
					        _ => bail!("Expected object - got scalar value."),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let properties = &schema.properties;
 | 
				
			||||||
 | 
					    let additional_properties = schema.additional_properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (key, value) in map {
 | 
				
			||||||
 | 
					        if let Some((_optional, prop_schema)) = properties.get::<str>(key) {
 | 
				
			||||||
 | 
					            match prop_schema.as_ref() {
 | 
				
			||||||
 | 
					                Schema::Object(object_schema) => {
 | 
				
			||||||
 | 
					                    verify_json_object(value, object_schema)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Schema::Array(array_schema) => {
 | 
				
			||||||
 | 
					                    verify_json_array(value, array_schema)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => verify_json(value, prop_schema)?,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if !additional_properties {
 | 
				
			||||||
 | 
					                bail!("property '{}': schema does not allow additional properties.", key);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (name, (optional, _prop_schema)) in properties {
 | 
				
			||||||
 | 
					        if *optional == false && data[name] == Value::Null {
 | 
				
			||||||
 | 
					            bail!("property '{}': property is missing and it is not optional.", name);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_schema1() {
 | 
					fn test_schema1() {
 | 
				
			||||||
    let schema = Schema::Object(ObjectSchema {
 | 
					    let schema = Schema::Object(ObjectSchema {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user