api/macro: add NumberSchema

Adapted from the integer schema, uses f64 type.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Cc: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2019-12-18 11:14:57 +01:00 committed by Wolfgang Bumiller
parent 81cac5fc29
commit 13007df318
4 changed files with 107 additions and 0 deletions

View File

@ -17,6 +17,9 @@ mod structs;
pub const INTNAMES: &[&str] = &[
"Integer", "i8", "i16", "i32", "i64", "isize", "u8", "u16", "u32", "u64", "usize",
];
pub const NUMBERNAMES: &[&str] = &[
"Number", "f32", "f64",
];
/// The main `Schema` type.
///
@ -151,6 +154,7 @@ enum SchemaItem {
Null,
Boolean,
Integer,
Number,
String,
Object(SchemaObject),
Array(SchemaArray),
@ -202,6 +206,8 @@ impl SchemaItem {
Ok(SchemaItem::Boolean)
} else if INTNAMES.iter().any(|n| name == n) {
Ok(SchemaItem::Integer)
} else if NUMBERNAMES.iter().any(|n| name == n) {
Ok(SchemaItem::Number)
} else if name == "String" {
Ok(SchemaItem::String)
} else if name == "Object" {
@ -235,6 +241,10 @@ impl SchemaItem {
let description = description?;
ts.extend(quote! { ::proxmox::api::schema::IntegerSchema::new(#description) });
}
SchemaItem::Number => {
let description = description?;
ts.extend(quote! { ::proxmox::api::schema::NumberSchema::new(#description) });
}
SchemaItem::String => {
let description = description?;
ts.extend(quote! { ::proxmox::api::schema::StringSchema::new(#description) });

View File

@ -204,6 +204,9 @@ fn handle_function_signature(
} else if super::INTNAMES.iter().any(|n| path.path.is_ident(n)) {
schema.item = SchemaItem::Integer;
continue;
} else if super::NUMBERNAMES.iter().any(|n| path.path.is_ident(n)) {
schema.item = SchemaItem::Number;
continue;
}
}
_ => (),

View File

@ -87,6 +87,12 @@ pub fn get_schema_type_text(schema: &Schema, _style: ParameterDisplayStyle) -> S
(None, Some(max)) => format!("<integer> (-N - {})", max),
_ => String::from("<integer>"),
},
Schema::Number(number_schema) => match (number_schema.minimum, number_schema.maximum) {
(Some(min), Some(max)) => format!("<number> ({} - {})", min, max),
(Some(min), None) => format!("<number> ({} - N)", min),
(None, Some(max)) => format!("<number> (-N - {})", max),
_ => String::from("<integer>"),
},
Schema::Object(_) => String::from("<object>"),
Schema::Array(_) => String::from("<array>"),
}
@ -106,6 +112,7 @@ pub fn get_property_description(
Schema::String(ref schema) => (schema.description, schema.default.map(|v| v.to_owned())),
Schema::Boolean(ref schema) => (schema.description, schema.default.map(|v| v.to_string())),
Schema::Integer(ref schema) => (schema.description, schema.default.map(|v| v.to_string())),
Schema::Number(ref schema) => (schema.description, schema.default.map(|v| v.to_string())),
Schema::Object(ref schema) => (schema.description, None),
Schema::Array(ref schema) => (schema.description, None),
};
@ -211,6 +218,10 @@ fn dump_api_return_schema(schema: &Schema) -> String {
let description = wrap_text("", "", schema.description, 80);
res.push_str(&description);
}
Schema::Number(schema) => {
let description = wrap_text("", "", schema.description, 80);
res.push_str(&description);
}
Schema::String(schema) => {
let description = wrap_text("", "", schema.description, 80);
res.push_str(&description);

View File

@ -155,6 +155,73 @@ impl IntegerSchema {
}
}
/// Data type to describe (JSON like) number value
#[derive(Debug)]
pub struct NumberSchema {
pub description: &'static str,
/// Optional minimum.
pub minimum: Option<f64>,
/// Optional maximum.
pub maximum: Option<f64>,
/// Optional default.
pub default: Option<f64>,
}
impl NumberSchema {
pub const fn new(description: &'static str) -> Self {
NumberSchema {
description,
default: None,
minimum: None,
maximum: None,
}
}
pub const fn default(mut self, default: f64) -> Self {
self.default = Some(default);
self
}
pub const fn minimum(mut self, minimum: f64) -> Self {
self.minimum = Some(minimum);
self
}
pub const fn maximum(mut self, maximium: f64) -> Self {
self.maximum = Some(maximium);
self
}
pub const fn schema(self) -> Schema {
Schema::Number(self)
}
fn check_constraints(&self, value: f64) -> Result<(), Error> {
if let Some(minimum) = self.minimum {
if value < minimum {
bail!(
"value must have a minimum value of {} (got {})",
minimum,
value
);
}
}
if let Some(maximum) = self.maximum {
if value > maximum {
bail!(
"value must have a maximum value of {} (got {})",
maximum,
value
);
}
}
Ok(())
}
}
/// Data type to describe string values.
#[derive(Debug)]
pub struct StringSchema {
@ -402,6 +469,7 @@ pub enum Schema {
Null,
Boolean(BooleanSchema),
Integer(IntegerSchema),
Number(NumberSchema),
String(StringSchema),
Object(ObjectSchema),
Array(ArraySchema),
@ -550,6 +618,11 @@ pub fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Err
integer_schema.check_constraints(res)?;
Value::Number(res.into())
}
Schema::Number(number_schema) => {
let res: f64 = value_str.parse()?;
number_schema.check_constraints(res)?;
Value::Number(serde_json::Number::from_f64(res).unwrap())
}
Schema::String(string_schema) => {
string_schema.check_constraints(value_str)?;
Value::String(value_str.into())
@ -683,6 +756,7 @@ pub fn verify_json(data: &Value, schema: &Schema) -> Result<(), Error> {
}
Schema::Boolean(boolean_schema) => verify_json_boolean(data, &boolean_schema)?,
Schema::Integer(integer_schema) => verify_json_integer(data, &integer_schema)?,
Schema::Number(number_schema) => verify_json_number(data, &number_schema)?,
Schema::String(string_schema) => verify_json_string(data, &string_schema)?,
}
Ok(())
@ -714,6 +788,15 @@ pub fn verify_json_integer(data: &Value, schema: &IntegerSchema) -> Result<(), E
}
}
/// Verify JSON value using an `NumberSchema`.
pub fn verify_json_number(data: &Value, schema: &NumberSchema) -> Result<(), Error> {
if let Some(value) = data.as_f64() {
schema.check_constraints(value)
} else {
bail!("Expected number value.");
}
}
/// Verify JSON value using an `ArraySchema`.
pub fn verify_json_array(data: &Value, schema: &ArraySchema) -> Result<(), Error> {
let list = match data {