From 7edeec7b06f9827eede541a7ffab7f3856f764e5 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 23 Nov 2018 11:34:15 +0100 Subject: [PATCH] remove parameter macro, implement ObjectSchema builder And store "optional" attribute inside properties hash. --- src/api/registry.rs | 3 +- src/api/schema.rs | 192 +++++++++++++++++++++----------------------- src/api3.rs | 13 +-- src/getopts.rs | 15 ++-- src/main.rs | 11 +-- 5 files changed, 112 insertions(+), 122 deletions(-) diff --git a/src/api/registry.rs b/src/api/registry.rs index 6725cdaf..7f297ee7 100644 --- a/src/api/registry.rs +++ b/src/api/registry.rs @@ -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", diff --git a/src/api/schema.rs b/src/api/schema.rs index a61de4ed..1691354d 100644 --- a/src/api/schema.rs +++ b/src/api/schema.rs @@ -41,7 +41,6 @@ impl fmt::Display for ParameterError { #[derive(Debug)] pub struct BooleanSchema { pub description: &'static str, - pub optional: bool, pub default: Option, } @@ -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, pub maximum: Option, pub default: Option, @@ -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, pub max_length: Option, @@ -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, } #[derive(Debug)] pub struct ObjectSchema { pub description: &'static str, - pub optional: bool, pub additional_properties: bool, - pub properties: HashMap<&'static str, Arc>, + pub properties: HashMap<&'static str, (bool, Arc)>, +} + +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) -> Self { + self.properties.insert(name, (false, schema)); + self + } + + pub fn optional(mut self, name: &'static str, schema: Arc) -> Self { + self.properties.insert(name, (true, schema)); + self + } + + pub fn arc(self) -> Arc { + Arc::new(self.into()) + } } #[derive(Debug)] @@ -213,6 +221,12 @@ impl From for Schema { } } +impl From for Schema { + fn from(object_schema: ObjectSchema) -> Self { + Schema::Object(object_schema) + } +} + pub enum ApiStringFormat { Enum(Vec), Pattern(Box), @@ -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>::new(), - } - }}; - ($($name:ident => $e:expr),*) => {{ - ObjectSchema { - description: "", - optional: false, - additional_properties: false, - properties: { - let mut map = HashMap::<&'static str, Arc>::new(); - $( - map.insert(stringify!($name), $e); - )* - map - } - } - }}; -} - pub fn parse_boolean(value_str: &str) -> Result { 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::(key) { + if let Some((_optional, prop_schema)) = properties.get::(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()); diff --git a/src/api3.rs b/src/api3.rs index 96d2dc92..62e89868 100644 --- a/src/api3.rs +++ b/src/api3.rs @@ -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({ diff --git a/src/getopts.rs b/src/getopts.rs index e88e3fa1..32a0dad5 100644 --- a/src/getopts.rs +++ b/src/getopts.rs @@ -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::(&name) { + if let Some((_optional, param_schema)) = properties.get::(&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(); diff --git a/src/main.rs b/src/main.rs index 8f060193..94398922 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 = std::env::args().skip(1).collect(); match getopts::parse_arguments(&args, &vec![], &schema) {