remove parameter macro, implement ObjectSchema builder

And store "optional" attribute inside properties hash.
This commit is contained in:
Dietmar Maurer 2018-11-23 11:34:15 +01:00
parent 82df76fff0
commit 7edeec7b06
5 changed files with 112 additions and 122 deletions

View File

@ -71,8 +71,7 @@ impl Registry {
"pve-vmid",
IntegerSchema::new("The (unique) ID of the VM.")
.minimum(1)
.optional(false)
);
);
self.register_option(
"pve-node",

View File

@ -41,7 +41,6 @@ impl fmt::Display for ParameterError {
#[derive(Debug)]
pub struct BooleanSchema {
pub description: &'static str,
pub optional: bool,
pub default: Option<bool>,
}
@ -50,16 +49,10 @@ impl BooleanSchema {
pub fn new(description: &'static str) -> Self {
BooleanSchema {
description: description,
optional: false,
default: None,
}
}
pub fn optional(mut self, optional: bool) -> Self {
self.optional = optional;
self
}
pub fn default(mut self, default: bool) -> Self {
self.default = Some(default);
self
@ -73,7 +66,6 @@ impl BooleanSchema {
#[derive(Debug)]
pub struct IntegerSchema {
pub description: &'static str,
pub optional: bool,
pub minimum: Option<isize>,
pub maximum: Option<isize>,
pub default: Option<isize>,
@ -84,18 +76,12 @@ impl IntegerSchema {
pub fn new(description: &'static str) -> Self {
IntegerSchema {
description: description,
optional: false,
default: None,
minimum: None,
maximum: None,
}
}
pub fn optional(mut self, optional: bool) -> Self {
self.optional = optional;
self
}
pub fn default(mut self, default: isize) -> Self {
self.default = Some(default);
self
@ -120,7 +106,6 @@ impl IntegerSchema {
#[derive(Debug)]
pub struct StringSchema {
pub description: &'static str,
pub optional: bool,
pub default: Option<&'static str>,
pub min_length: Option<usize>,
pub max_length: Option<usize>,
@ -132,7 +117,6 @@ impl StringSchema {
pub fn new(description: &'static str) -> Self {
StringSchema {
description: description,
optional: false,
default: None,
min_length: None,
max_length: None,
@ -140,11 +124,6 @@ impl StringSchema {
}
}
pub fn optional(mut self, optional: bool) -> Self {
self.optional = optional;
self
}
pub fn default(mut self, text: &'static str) -> Self {
self.default = Some(text);
self
@ -173,16 +152,45 @@ impl StringSchema {
#[derive(Debug)]
pub struct ArraySchema {
pub description: &'static str,
pub optional: bool,
pub items: Arc<Schema>,
}
#[derive(Debug)]
pub struct ObjectSchema {
pub description: &'static str,
pub optional: bool,
pub additional_properties: bool,
pub properties: HashMap<&'static str, Arc<Schema>>,
pub properties: HashMap<&'static str, (bool, Arc<Schema>)>,
}
impl ObjectSchema {
pub fn new(description: &'static str) -> Self {
let properties = HashMap::new();
ObjectSchema {
description: description,
additional_properties: false,
properties: properties,
}
}
pub fn additional_properties(mut self, additional_properties: bool) -> Self {
self.additional_properties = additional_properties;
self
}
pub fn required(mut self, name: &'static str, schema: Arc<Schema>) -> Self {
self.properties.insert(name, (false, schema));
self
}
pub fn optional(mut self, name: &'static str, schema: Arc<Schema>) -> Self {
self.properties.insert(name, (true, schema));
self
}
pub fn arc(self) -> Arc<Schema> {
Arc::new(self.into())
}
}
#[derive(Debug)]
@ -213,6 +221,12 @@ impl From<IntegerSchema> for Schema {
}
}
impl From<ObjectSchema> for Schema {
fn from(object_schema: ObjectSchema) -> Self {
Schema::Object(object_schema)
}
}
pub enum ApiStringFormat {
Enum(Vec<String>),
Pattern(Box<Regex>),
@ -239,33 +253,6 @@ impl std::fmt::Debug for ApiStringFormat {
}
}
#[macro_export]
macro_rules! parameter {
() => {{
ObjectSchema {
description: "",
optional: false,
additional_properties: false,
properties: HashMap::<&'static str, Arc<Schema>>::new(),
}
}};
($($name:ident => $e:expr),*) => {{
ObjectSchema {
description: "",
optional: false,
additional_properties: false,
properties: {
let mut map = HashMap::<&'static str, Arc<Schema>>::new();
$(
map.insert(stringify!($name), $e);
)*
map
}
}
}};
}
pub fn parse_boolean(value_str: &str) -> Result<bool, Error> {
match value_str.to_lowercase().as_str() {
"1" | "on" | "yes" | "true" => Ok(true),
@ -357,7 +344,7 @@ pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &ObjectSche
let additional_properties = schema.additional_properties;
for (key, value) in data {
if let Some(prop_schema) = properties.get::<str>(key) {
if let Some((_optional, prop_schema)) = properties.get::<str>(key) {
match prop_schema.as_ref() {
Schema::Array(array_schema) => {
if params[key] == Value::Null {
@ -408,16 +395,8 @@ pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &ObjectSche
}
if test_required && errors.len() == 0 {
for (name, prop_schema) in properties {
let optional = match prop_schema.as_ref() {
Schema::Boolean(boolean_schema) => boolean_schema.optional,
Schema::Integer(integer_schema) => integer_schema.optional,
Schema::String(string_schema) => string_schema.optional,
Schema::Array(array_schema) => array_schema.optional,
Schema::Object(object_schema) => object_schema.optional,
Schema::Null => true,
};
if optional == false && params[name] == Value::Null {
for (name, (optional, _prop_schema)) in properties {
if *optional == false && params[name] == Value::Null {
errors.push(format_err!("parameter '{}': parameter is missing and it is not optional.", name));
}
}
@ -442,7 +421,6 @@ pub fn parse_query_string(query: &str, schema: &ObjectSchema, test_required: boo
fn test_schema1() {
let schema = Schema::Object(ObjectSchema {
description: "TEST",
optional: false,
additional_properties: false,
properties: {
let map = HashMap::new();
@ -457,25 +435,27 @@ fn test_schema1() {
#[test]
fn test_query_string() {
let schema = parameter!{name => StringSchema::new("Name.").optional(false).arc()};
let schema = ObjectSchema::new("Parameters.")
.required("name", StringSchema::new("Name.").arc());
let res = parse_query_string("", &schema, true);
assert!(res.is_err());
let schema = parameter!{name => StringSchema::new("Name.").optional(true).arc()};
let schema = ObjectSchema::new("Parameters.")
.optional("name", StringSchema::new("Name.").arc());
let res = parse_query_string("", &schema, true);
assert!(res.is_ok());
// TEST min_length and max_length
let schema = parameter!{
name => StringSchema::new("Name.")
.optional(false)
.min_length(5)
.max_length(10)
.arc()
};
let schema = ObjectSchema::new("Parameters.")
.required(
"name", StringSchema::new("Name.")
.min_length(5)
.max_length(10)
.arc()
);
let res = parse_query_string("name=abcd", &schema, true);
assert!(res.is_err());
@ -491,12 +471,12 @@ fn test_query_string() {
// TEST regex pattern
let schema = parameter!{
name => StringSchema::new("Name.")
.optional(false)
.format(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("test").unwrap()))))
.arc()
};
let schema = ObjectSchema::new("Parameters.")
.required(
"name", StringSchema::new("Name.")
.format(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("test").unwrap()))))
.arc()
);
let res = parse_query_string("name=abcd", &schema, true);
assert!(res.is_err());
@ -504,12 +484,12 @@ fn test_query_string() {
let res = parse_query_string("name=ateststring", &schema, true);
assert!(res.is_ok());
let schema = parameter!{
name => StringSchema::new("Name.")
.optional(false)
.format(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("^test$").unwrap()))))
.arc()
};
let schema = ObjectSchema::new("Parameters.")
.required(
"name", StringSchema::new("Name.")
.format(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("^test$").unwrap()))))
.arc()
);
let res = parse_query_string("name=ateststring", &schema, true);
assert!(res.is_err());
@ -519,12 +499,12 @@ fn test_query_string() {
// TEST string enums
let schema = parameter!{
name => StringSchema::new("Name.")
.optional(false)
.format(Arc::new(ApiStringFormat::Enum(vec!["ev1".into(), "ev2".into()])))
.arc()
};
let schema = ObjectSchema::new("Parameters.")
.required(
"name", StringSchema::new("Name.")
.format(Arc::new(ApiStringFormat::Enum(vec!["ev1".into(), "ev2".into()])))
.arc()
);
let res = parse_query_string("name=noenum", &schema, true);
assert!(res.is_err());
@ -543,18 +523,22 @@ fn test_query_string() {
#[test]
fn test_query_integer() {
let schema = parameter!{count => IntegerSchema::new("Count.").optional(false).arc()};
let schema = ObjectSchema::new("Parameters.")
.required(
"count" , IntegerSchema::new("Count.")
.arc()
);
let res = parse_query_string("", &schema, true);
assert!(res.is_err());
let schema = parameter!{
count => IntegerSchema::new("Count.")
.optional(true)
.minimum(-3)
.maximum(50)
.arc()
};
let schema = ObjectSchema::new("Parameters.")
.optional(
"count", IntegerSchema::new("Count.")
.minimum(-3)
.maximum(50)
.arc()
);
let res = parse_query_string("", &schema, true);
assert!(res.is_ok());
@ -584,12 +568,20 @@ fn test_query_integer() {
#[test]
fn test_query_boolean() {
let schema = parameter!{force => BooleanSchema::new("Force.").optional(false).arc()};
let schema = ObjectSchema::new("Parameters.")
.required(
"force", BooleanSchema::new("Force.")
.arc()
);
let res = parse_query_string("", &schema, true);
assert!(res.is_err());
let schema = parameter!{force => BooleanSchema::new("Force.").optional(true).arc()};
let schema = ObjectSchema::new("Parameters.")
.optional(
"force", BooleanSchema::new("Force.")
.arc()
);
let res = parse_query_string("", &schema, true);
assert!(res.is_ok());

View File

@ -31,7 +31,7 @@ pub fn router() -> Router {
let route3 = Router::new()
.get(ApiMethod {
description: "Another Endpoint.",
parameters: parameter!{},
parameters: ObjectSchema::new("Another Endpoint."),
returns: Schema::Null,
handler: |param, _info| {
println!("This is a clousure handler: {}", param);
@ -44,11 +44,12 @@ pub fn router() -> Router {
.get(ApiMethod {
handler: test_sync_api_handler,
description: "This is a simple test.",
parameters: parameter!{
force => BooleanSchema::new("Test for boolean options")
.optional(true)
.arc()
},
parameters: ObjectSchema::new("This is a simple test.")
.optional(
"force",
BooleanSchema::new("Test for boolean options")
.arc()
),
returns: Schema::Null,
})
.subdirs({

View File

@ -75,7 +75,7 @@ pub fn parse_arguments(
None => {
let mut want_bool = false;
let mut can_default = false;
if let Some(param_schema) = properties.get::<str>(&name) {
if let Some((_optional, param_schema)) = properties.get::<str>(&name) {
if let Schema::Boolean(boolean_schema) = param_schema.as_ref() {
want_bool = true;
if let Some(default) = boolean_schema.default {
@ -156,7 +156,11 @@ pub fn parse_arguments(
#[test]
fn test_boolean_arg() {
let schema = parameter!{enable => BooleanSchema::new("Enable").optional(false).arc()};
let schema = ObjectSchema::new("Parameters:")
.required(
"enable", BooleanSchema::new("Enable")
.arc()
);
let mut variants: Vec<(Vec<&str>, bool)> = vec![];
variants.push((vec!["-enable"], true));
@ -186,10 +190,9 @@ fn test_boolean_arg() {
#[test]
fn test_argument_paramenter() {
let schema = parameter!{
enable => BooleanSchema::new("Enable.").optional(false).arc(),
storage => StringSchema::new("Storatge:").optional(false).arc()
};
let schema = ObjectSchema::new("Parameters:")
.required("enable", BooleanSchema::new("Enable.").arc())
.required("storage", StringSchema::new("Storage.").arc());
let args = vec!["-enable", "local"];
let string_args = args.iter().map(|s| s.to_string()).collect();

View File

@ -1,9 +1,5 @@
#[macro_use]
extern crate apitest;
use std::collections::HashMap;
use std::sync::Arc;
use apitest::api::schema::*;
use apitest::api::router::*;
use apitest::api::config::*;
@ -23,10 +19,9 @@ fn main() {
let prop = StringSchema::new("This is a test").arc();
//let prop = Arc::new(ApiString!{ optional => true });
let schema = parameter!{
name1 => prop.clone(),
name2 => prop.clone()
};
let schema = ObjectSchema::new("Parameters.")
.required("name1", prop.clone())
.required("name2", prop.clone());
let args: Vec<String> = std::env::args().skip(1).collect();
match getopts::parse_arguments(&args, &vec![], &schema) {