implement simple schema parser

This commit is contained in:
Dietmar Maurer 2018-11-06 13:10:10 +01:00
parent 42d569c186
commit cfa5886c33
3 changed files with 118 additions and 18 deletions

View File

@ -34,7 +34,7 @@ pub fn router() -> MethodInfo {
description: "This is a simple test.", description: "This is a simple test.",
parameters: parameter!{ parameters: parameter!{
force => Boolean!{ force => Boolean!{
optional => Some(true), optional => true,
description => "Test for boolean options." description => "Test for boolean options."
} }
}, },

View File

@ -7,23 +7,23 @@ pub type PropertyMap = HashMap<&'static str, Jss>;
#[derive(Debug)] #[derive(Debug)]
pub struct JssBoolean { pub struct JssBoolean {
pub description: &'static str, pub description: &'static str,
pub optional: Option<bool>, pub optional: bool,
pub default: Option<bool>, pub default: Option<bool>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct JssInteger { pub struct JssInteger {
pub description: &'static str, pub description: &'static str,
pub optional: Option<bool>, pub optional: bool,
pub minimum: Option<usize>, pub minimum: Option<isize>,
pub maximum: Option<usize>, pub maximum: Option<isize>,
pub default: Option<usize>, pub default: Option<isize>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct JssString { pub struct JssString {
pub description: &'static str, pub description: &'static str,
pub optional: Option<bool>, pub optional: bool,
pub default: Option<&'static str>, pub default: Option<&'static str>,
pub min_length: Option<usize>, pub min_length: Option<usize>,
pub max_length: Option<usize>, pub max_length: Option<usize>,
@ -32,15 +32,15 @@ pub struct JssString {
#[derive(Debug)] #[derive(Debug)]
pub struct JssArray { pub struct JssArray {
pub description: &'static str, pub description: &'static str,
pub optional: Option<bool>, pub optional: bool,
pub items: Box<Jss>, pub items: Box<Jss>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct JssObject { pub struct JssObject {
pub description: &'static str, pub description: &'static str,
pub optional: Option<bool>, pub optional: bool,
pub additional_properties: Option<bool>, pub additional_properties: bool,
pub properties: HashMap<&'static str, Jss>, pub properties: HashMap<&'static str, Jss>,
} }
@ -56,7 +56,7 @@ pub enum Jss {
pub const DEFAULTBOOL: JssBoolean = JssBoolean { pub const DEFAULTBOOL: JssBoolean = JssBoolean {
description: "", description: "",
optional: None, optional: false,
default: None, default: None,
}; };
@ -69,7 +69,7 @@ macro_rules! Boolean {
pub const DEFAULTINTEGER: JssInteger = JssInteger { pub const DEFAULTINTEGER: JssInteger = JssInteger {
description: "", description: "",
optional: None, optional: false,
default: None, default: None,
minimum: None, minimum: None,
maximum: None, maximum: None,
@ -84,7 +84,7 @@ macro_rules! Integer {
pub const DEFAULTSTRING: JssString = JssString { pub const DEFAULTSTRING: JssString = JssString {
description: "", description: "",
optional: None, optional: false,
default: None, default: None,
min_length: None, min_length: None,
max_length: None, max_length: None,
@ -102,8 +102,8 @@ macro_rules! parameter {
($($name:ident => $e:expr),*) => {{ ($($name:ident => $e:expr),*) => {{
let inner = JssObject { let inner = JssObject {
description: "", description: "",
optional: None, optional: false,
additional_properties: None, additional_properties: false,
properties: { properties: {
let mut map = HashMap::<&'static str, Jss>::new(); let mut map = HashMap::<&'static str, Jss>::new();
$( $(
@ -117,12 +117,106 @@ macro_rules! parameter {
}} }}
} }
fn parse_simple_value(value_str: &str, schema: &Jss) -> Result<Value, Error> {
pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &Jss) -> Result<Value, Error> { let value = match schema {
Jss::Null => {
bail!("parse_parameter_strings: internal error - schema contains Null.");
}
Jss::Boolean(jss_boolean) => {
let res = match value_str.to_lowercase().as_str() {
"1" | "on" | "yes" | "true" => true,
"0" | "off" | "no" | "false" => false,
_ => bail!("Unable to parse boolean option."),
};
Value::Bool(res)
}
Jss::Integer(jss_integer) => {
let res: isize = value_str.parse()?;
Value::Number(res.into())
}
Jss::String(jss_string) => {
Value::String(value_str.into())
}
_ => bail!("parse_simple_value: schema contains complex Objects."),
};
Ok(value)
}
pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &Jss) -> Result<Value, Vec<Error>> {
println!("QUERY Strings {:?}", data); println!("QUERY Strings {:?}", data);
Ok(json!(null)) let mut params = json!({});
let mut errors: Vec<Error> = Vec::new();
match schema {
Jss::Object(JssObject { properties, additional_properties, .. }) => {
for (key, value) in data {
if let Some(prop_schema) = properties.get::<str>(key) {
match prop_schema {
Jss::Object(_) => {
errors.push(format_err!("parameter {}: cant parse complex Objects.", key));
}
Jss::Array(jss_array) => {
if params[key] == Value::Null {
params[key] = json!([]);
}
match params[key] {
Value::Array(ref mut array) => {
match parse_simple_value(value, &jss_array.items) {
Ok(res) => array.push(res),
Err(err) => errors.push(format_err!("parameter {}: {}", key, err)),
}
}
_ => errors.push(format_err!("parameter {}: expected array - type missmatch", key)),
}
}
_ => {
match parse_simple_value(value, prop_schema) {
Ok(res) => {
if params[key] == Value::Null {
params[key] = res;
} else {
errors.push(format_err!("parameter {}: duplicate parameter.", key));
}
},
Err(err) => errors.push(format_err!("parameter {}: {}", key, err)),
}
}
}
} else {
if *additional_properties {
match params[key] {
Value::Null => {
params[key] = Value::String(value.to_owned());
},
Value::String(ref old) => {
params[key] = Value::Array(
vec![Value::String(old.to_owned()), Value::String(value.to_owned())]);
}
Value::Array(ref mut array) => {
array.push(Value::String(value.to_string()));
}
_ => errors.push(format_err!("parameter {}: expected array - type missmatch", key)),
}
} else {
errors.push(format_err!("parameter {}: schema does not allow additional properties.", key));
}
}
}
}
_ => errors.push(format_err!("Got unexpected schema type in parse_parameter_strings.")),
}
if (errors.len() > 0) {
Err(errors)
} else {
Ok(params)
}
} }
#[test] #[test]

View File

@ -70,7 +70,13 @@ fn handle_request(req: Request<Body>) -> Response<Body> {
match parse_parameter_strings(&param_list, &api_method.parameters) { match parse_parameter_strings(&param_list, &api_method.parameters) {
Ok(query) => query, Ok(query) => query,
Err(err) => http_error!(NOT_FOUND, format!("Unable to parse query parameters '{}' - {}", data, err)), Err(ref error_list) => {
let mut msg = String::from("");
for item in error_list {
msg = msg + &item.to_string() + "\n";
}
http_error!(BAD_REQUEST, msg);
}
} }
} }
None => json!({}), None => json!({}),