remove parameter macro, implement ObjectSchema builder
And store "optional" attribute inside properties hash.
This commit is contained in:
		| @ -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", | ||||
|  | ||||
| @ -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()); | ||||
|  | ||||
							
								
								
									
										13
									
								
								src/api3.rs
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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({ | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
							
								
								
									
										11
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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<String> = std::env::args().skip(1).collect(); | ||||
|     match getopts::parse_arguments(&args, &vec![], &schema) { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user