/api/schema.rs: implement Schema::Option

This commit is contained in:
Dietmar Maurer 2019-01-18 17:40:37 +01:00
parent fdb95d92dd
commit 0a35462c1e
4 changed files with 44 additions and 20 deletions

View File

@ -251,7 +251,7 @@ impl ArraySchema {
pub struct ObjectSchema { pub struct ObjectSchema {
pub description: &'static str, pub description: &'static str,
pub additional_properties: bool, pub additional_properties: bool,
pub properties: HashMap<&'static str, (bool, Arc<Schema>)>, pub properties: HashMap<&'static str, Arc<Schema>>,
pub default_key: Option<&'static str>, pub default_key: Option<&'static str>,
} }
@ -278,12 +278,12 @@ impl ObjectSchema {
} }
pub fn required<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self { pub fn required<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self {
self.properties.insert(name, (false, schema.into())); self.properties.insert(name, schema.into());
self self
} }
pub fn optional<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self { pub fn optional<S: Into<Arc<Schema>>>(mut self, name: &'static str, schema: S) -> Self {
self.properties.insert(name, (true, schema.into())); self.properties.insert(name, Arc::new(Schema::Option(schema.into())));
self self
} }
} }
@ -296,6 +296,7 @@ pub enum Schema {
String(StringSchema), String(StringSchema),
Object(ObjectSchema), Object(ObjectSchema),
Array(ArraySchema), Array(ArraySchema),
Option(Arc<Schema>),
} }
impl From<StringSchema> for Schema { impl From<StringSchema> for Schema {
@ -455,6 +456,9 @@ pub fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Err
string_schema.check_constraints(value_str)?; string_schema.check_constraints(value_str)?;
Value::String(value_str.into()) Value::String(value_str.into())
} }
Schema::Option(option_schema) => {
parse_simple_value(value_str, option_schema)?
}
_ => bail!("unable to parse complex (sub) objects."), _ => bail!("unable to parse complex (sub) objects."),
}; };
Ok(value) Ok(value)
@ -472,7 +476,7 @@ pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &ObjectSche
let additional_properties = schema.additional_properties; let additional_properties = schema.additional_properties;
for (key, value) in data { for (key, value) in data {
if let Some((_optional, prop_schema)) = properties.get::<str>(key) { if let Some(prop_schema) = properties.get::<str>(key) {
match prop_schema.as_ref() { match prop_schema.as_ref() {
Schema::Array(array_schema) => { Schema::Array(array_schema) => {
if params[key] == Value::Null { if params[key] == Value::Null {
@ -523,12 +527,17 @@ pub fn parse_parameter_strings(data: &Vec<(String, String)>, schema: &ObjectSche
} }
if test_required && errors.len() == 0 { if test_required && errors.len() == 0 {
for (name, (optional, _prop_schema)) in properties { for (name, prop_schema) in properties {
if *optional == false && params[name] == Value::Null { match prop_schema.as_ref() {
Schema::Option(_) => {},
_ => {
if params[name] == Value::Null {
errors.push(format_err!("parameter '{}': parameter is missing and it is not optional.", name)); errors.push(format_err!("parameter '{}': parameter is missing and it is not optional.", name));
} }
} }
} }
}
}
if errors.len() > 0 { if errors.len() > 0 {
Err(errors) Err(errors)
@ -554,6 +563,11 @@ pub fn verify_json(data: &Value, schema: &Schema) -> Result<(), Error> {
Schema::Array(array_schema) => { Schema::Array(array_schema) => {
verify_json_array(data, &array_schema)?; verify_json_array(data, &array_schema)?;
} }
Schema::Option(option_schema) => {
if !data.is_null() {
verify_json(data, option_schema)?;
}
}
Schema::Null => { Schema::Null => {
if !data.is_null() { if !data.is_null() {
bail!("Expected Null, but value is not Null."); bail!("Expected Null, but value is not Null.");
@ -618,7 +632,7 @@ pub fn verify_json_object(data: &Value, schema: &ObjectSchema) -> Result<(), Err
let additional_properties = schema.additional_properties; let additional_properties = schema.additional_properties;
for (key, value) in map { for (key, value) in map {
if let Some((_optional, prop_schema)) = properties.get::<str>(key) { if let Some(prop_schema) = properties.get::<str>(key) {
match prop_schema.as_ref() { match prop_schema.as_ref() {
Schema::Object(object_schema) => { Schema::Object(object_schema) => {
verify_json_object(value, object_schema)?; verify_json_object(value, object_schema)?;
@ -635,11 +649,16 @@ pub fn verify_json_object(data: &Value, schema: &ObjectSchema) -> Result<(), Err
} }
} }
for (name, (optional, _prop_schema)) in properties { for (name, prop_schema) in properties {
if *optional == false && data[name] == Value::Null { match prop_schema.as_ref() {
Schema::Option(_) => {},
_ => {
if data[name] == Value::Null {
bail!("property '{}': property is missing and it is not optional.", name); bail!("property '{}': property is missing and it is not optional.", name);
} }
} }
}
}
Ok(()) Ok(())
} }

View File

@ -120,7 +120,7 @@ fn record_done_arguments(done: &mut HashSet<String>, parameters: &ObjectSchema,
for arg in list { for arg in list {
if arg.starts_with("--") && arg.len() > 2 { if arg.starts_with("--") && arg.len() > 2 {
let prop_name = arg[2..].to_owned(); let prop_name = arg[2..].to_owned();
if let Some((_, schema)) = parameters.properties.get::<str>(&prop_name) { if let Some(schema) = parameters.properties.get::<str>(&prop_name) {
match schema.as_ref() { match schema.as_ref() {
Schema::Array(_) => { /* do nothing */ } Schema::Array(_) => { /* do nothing */ }
_ => { done.insert(prop_name); } _ => { done.insert(prop_name); }
@ -147,7 +147,7 @@ fn print_simple_completion(
print_simple_completion(cli_cmd, done, &arg_param[1..], args); print_simple_completion(cli_cmd, done, &arg_param[1..], args);
return; return;
} else if args.len() == 1 { } else if args.len() == 1 {
if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) { if let Some(schema) = cli_cmd.info.parameters.properties.get(prop_name) {
print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &args[0]); print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &args[0]);
} }
} }
@ -164,14 +164,14 @@ fn print_simple_completion(
let last = &args[args.len()-1]; let last = &args[args.len()-1];
if last.starts_with("--") && last.len() > 2 { if last.starts_with("--") && last.len() > 2 {
let prop_name = &last[2..]; let prop_name = &last[2..];
if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) { if let Some(schema) = cli_cmd.info.parameters.properties.get(prop_name) {
print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &prefix); print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &prefix);
} }
return; return;
} }
} }
for (name, (_optional, _schema)) in &cli_cmd.info.parameters.properties { for (name, _schema) in &cli_cmd.info.parameters.properties {
if done.contains(*name) { continue; } if done.contains(*name) { continue; }
let option = String::from("--") + name; let option = String::from("--") + name;
if option.starts_with(&prefix) { if option.starts_with(&prefix) {

View File

@ -75,7 +75,7 @@ pub fn parse_arguments<T: AsRef<str>>(
None => { None => {
let mut want_bool = false; let mut want_bool = false;
let mut can_default = false; let mut can_default = false;
if let Some((_optional, param_schema)) = properties.get::<str>(&name) { if let Some(param_schema) = properties.get::<str>(&name) {
if let Schema::Boolean(boolean_schema) = param_schema.as_ref() { if let Schema::Boolean(boolean_schema) = param_schema.as_ref() {
want_bool = true; want_bool = true;
if let Some(default) = boolean_schema.default { if let Some(default) = boolean_schema.default {

View File

@ -150,11 +150,16 @@ impl SectionConfig {
let mut state = ParseState::BeforeHeader; let mut state = ParseState::BeforeHeader;
let test_required_properties = |value: &Value, schema: &ObjectSchema| -> Result<(), Error> { let test_required_properties = |value: &Value, schema: &ObjectSchema| -> Result<(), Error> {
for (name, (optional, _prop_schema)) in &schema.properties { for (name, prop_schema) in &schema.properties {
if *optional == false && value[name] == Value::Null { match prop_schema.as_ref() {
Schema::Option(_) => {},
_ => {
if value[name] == Value::Null {
return Err(format_err!("property '{}' is missing and it is not optional.", name)); return Err(format_err!("property '{}' is missing and it is not optional.", name));
} }
} }
}
}
Ok(()) Ok(())
}; };
@ -204,7 +209,7 @@ impl SectionConfig {
if let Some((key, value)) = (self.parse_section_content)(line) { if let Some((key, value)) = (self.parse_section_content)(line) {
//println!("CONTENT: key: {} value: {}", key, value); //println!("CONTENT: key: {} value: {}", key, value);
if let Some((_optional, prop_schema)) = plugin.properties.properties.get::<str>(&key) { if let Some(prop_schema) = plugin.properties.properties.get::<str>(&key) {
match parse_simple_value(&value, prop_schema) { match parse_simple_value(&value, prop_schema) {
Ok(value) => { Ok(value) => {
if config[&key] == Value::Null { if config[&key] == Value::Null {