2018-11-05 14:20:27 +00:00
|
|
|
use failure::*;
|
2018-11-03 14:10:21 +00:00
|
|
|
use std::collections::HashMap;
|
2018-11-05 14:20:27 +00:00
|
|
|
use serde_json::{json, Value};
|
2018-11-01 12:05:45 +00:00
|
|
|
|
2018-11-03 14:10:21 +00:00
|
|
|
pub type PropertyMap = HashMap<&'static str, Jss>;
|
2018-10-31 09:42:14 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub struct JssBoolean {
|
|
|
|
pub description: &'static str,
|
2018-11-06 12:10:10 +00:00
|
|
|
pub optional: bool,
|
2018-10-31 09:42:14 +00:00
|
|
|
pub default: Option<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub struct JssInteger {
|
|
|
|
pub description: &'static str,
|
2018-11-06 12:10:10 +00:00
|
|
|
pub optional: bool,
|
|
|
|
pub minimum: Option<isize>,
|
|
|
|
pub maximum: Option<isize>,
|
|
|
|
pub default: Option<isize>,
|
2018-10-31 09:42:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub struct JssString {
|
|
|
|
pub description: &'static str,
|
2018-11-06 12:10:10 +00:00
|
|
|
pub optional: bool,
|
2018-11-03 14:10:21 +00:00
|
|
|
pub default: Option<&'static str>,
|
2018-10-31 09:42:14 +00:00
|
|
|
pub min_length: Option<usize>,
|
|
|
|
pub max_length: Option<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub struct JssArray {
|
|
|
|
pub description: &'static str,
|
2018-11-06 12:10:10 +00:00
|
|
|
pub optional: bool,
|
2018-11-03 14:10:21 +00:00
|
|
|
pub items: Box<Jss>,
|
2018-10-31 09:42:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub struct JssObject {
|
|
|
|
pub description: &'static str,
|
2018-11-06 12:10:10 +00:00
|
|
|
pub optional: bool,
|
|
|
|
pub additional_properties: bool,
|
2018-11-05 14:20:27 +00:00
|
|
|
pub properties: HashMap<&'static str, Jss>,
|
2018-10-31 09:42:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2018-11-03 14:10:21 +00:00
|
|
|
pub enum Jss {
|
2018-10-31 09:42:14 +00:00
|
|
|
Null,
|
2018-11-03 14:10:21 +00:00
|
|
|
Boolean(JssBoolean),
|
|
|
|
Integer(JssInteger),
|
|
|
|
String(JssString),
|
|
|
|
Object(JssObject),
|
|
|
|
Array(JssArray),
|
2018-10-31 09:42:14 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 09:50:24 +00:00
|
|
|
pub const DEFAULTBOOL: JssBoolean = JssBoolean {
|
2018-10-31 09:42:14 +00:00
|
|
|
description: "",
|
2018-11-06 12:10:10 +00:00
|
|
|
optional: false,
|
2018-10-31 09:42:14 +00:00
|
|
|
default: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! Boolean {
|
|
|
|
($($name:ident => $e:expr),*) => {{
|
|
|
|
Jss::Boolean(JssBoolean { $($name: $e, )* ..DEFAULTBOOL})
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2018-11-03 09:50:24 +00:00
|
|
|
pub const DEFAULTINTEGER: JssInteger = JssInteger {
|
2018-10-31 09:42:14 +00:00
|
|
|
description: "",
|
2018-11-06 12:10:10 +00:00
|
|
|
optional: false,
|
2018-10-31 09:42:14 +00:00
|
|
|
default: None,
|
|
|
|
minimum: None,
|
|
|
|
maximum: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! Integer {
|
|
|
|
($($name:ident => $e:expr),*) => {{
|
|
|
|
Jss::Integer(JssInteger { $($name: $e, )* ..DEFAULTINTEGER})
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2018-11-03 09:50:24 +00:00
|
|
|
pub const DEFAULTSTRING: JssString = JssString {
|
2018-10-31 09:42:14 +00:00
|
|
|
description: "",
|
2018-11-06 12:10:10 +00:00
|
|
|
optional: false,
|
2018-10-31 09:42:14 +00:00
|
|
|
default: None,
|
|
|
|
min_length: None,
|
|
|
|
max_length: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! ApiString {
|
|
|
|
($($name:ident => $e:expr),*) => {{
|
|
|
|
Jss::String(JssString { $($name: $e, )* ..DEFAULTSTRING})
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[macro_export]
|
2018-11-03 14:10:21 +00:00
|
|
|
macro_rules! parameter {
|
2018-10-31 09:42:14 +00:00
|
|
|
($($name:ident => $e:expr),*) => {{
|
2018-11-03 14:10:21 +00:00
|
|
|
let inner = JssObject {
|
|
|
|
description: "",
|
2018-11-06 12:10:10 +00:00
|
|
|
optional: false,
|
|
|
|
additional_properties: false,
|
2018-11-03 14:10:21 +00:00
|
|
|
properties: {
|
|
|
|
let mut map = HashMap::<&'static str, Jss>::new();
|
|
|
|
$(
|
|
|
|
map.insert(stringify!($name), $e);
|
|
|
|
)*
|
2018-11-05 14:20:27 +00:00
|
|
|
map
|
2018-11-03 14:10:21 +00:00
|
|
|
}
|
|
|
|
};
|
2018-10-31 09:42:14 +00:00
|
|
|
|
2018-11-03 14:10:21 +00:00
|
|
|
Jss::Object(inner)
|
2018-10-31 09:42:14 +00:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2018-11-06 12:10:10 +00:00
|
|
|
fn parse_simple_value(value_str: &str, schema: &Jss) -> Result<Value, Error> {
|
2018-10-31 09:42:14 +00:00
|
|
|
|
2018-11-06 12:10:10 +00:00
|
|
|
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>> {
|
2018-11-05 14:20:27 +00:00
|
|
|
|
|
|
|
println!("QUERY Strings {:?}", data);
|
|
|
|
|
2018-11-06 12:10:10 +00:00
|
|
|
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)
|
|
|
|
}
|
2018-11-05 14:20:27 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 14:10:21 +00:00
|
|
|
#[test]
|
|
|
|
fn test_shema1() {
|
|
|
|
let schema = Jss::Object(JssObject {
|
|
|
|
description: "TEST",
|
|
|
|
optional: None,
|
|
|
|
additional_properties: None,
|
|
|
|
properties: {
|
|
|
|
let map = HashMap::new();
|
|
|
|
|
|
|
|
Box::new(map)
|
2018-11-02 08:44:18 +00:00
|
|
|
}
|
2018-11-03 14:10:21 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
println!("TEST Schema: {:?}", schema);
|
2018-11-02 08:44:18 +00:00
|
|
|
}
|
2018-11-03 08:08:01 +00:00
|
|
|
|
2018-11-03 14:10:21 +00:00
|
|
|
/*
|
2018-11-03 08:08:01 +00:00
|
|
|
#[test]
|
|
|
|
fn test_shema1() {
|
|
|
|
static PARAMETERS1: PropertyMap = propertymap!{
|
|
|
|
force => &Boolean!{
|
|
|
|
description => "Test for boolean options."
|
|
|
|
},
|
|
|
|
text1 => &ApiString!{
|
|
|
|
description => "A simple text string.",
|
|
|
|
min_length => Some(10),
|
|
|
|
max_length => Some(30)
|
|
|
|
},
|
|
|
|
count => &Integer!{
|
|
|
|
description => "A counter for everything.",
|
|
|
|
minimum => Some(0),
|
|
|
|
maximum => Some(10)
|
|
|
|
},
|
|
|
|
myarray1 => &Array!{
|
|
|
|
description => "Test Array of simple integers.",
|
|
|
|
items => &PVE_VMID
|
|
|
|
},
|
|
|
|
myarray2 => &Jss::Array(JssArray {
|
|
|
|
description: "Test Array of simple integers.",
|
|
|
|
optional: Some(false),
|
|
|
|
items: &Object!{description => "Empty Object."},
|
|
|
|
}),
|
|
|
|
myobject => &Object!{
|
|
|
|
description => "TEST Object.",
|
|
|
|
properties => &propertymap!{
|
|
|
|
vmid => &PVE_VMID,
|
|
|
|
loop => &Integer!{
|
|
|
|
description => "Totally useless thing.",
|
|
|
|
optional => Some(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
emptyobject => &Object!{description => "Empty Object."}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (k, v) in PARAMETERS1.entries {
|
|
|
|
println!("Parameter: {} Value: {:?}", k, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2018-11-03 14:10:21 +00:00
|
|
|
*/
|