api: macro: implement minimum/maximum_length checks

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-07-29 14:40:04 +02:00
parent f1f4c14819
commit b77733c8ef
3 changed files with 78 additions and 14 deletions

View File

@ -96,6 +96,10 @@ pub struct ParameterDefinition {
#[builder(default)]
pub minimum: Option<syn::Expr>,
#[builder(default)]
pub maximum_length: Option<syn::Expr>,
#[builder(default)]
pub minimum_length: Option<syn::Expr>,
#[builder(default)]
pub validate: Option<syn::Expr>,
}
@ -122,6 +126,12 @@ impl ParameterDefinition {
"minimum" => {
def.minimum(Some(value.expect_expr()?));
}
"maximum_length" => {
def.maximum_length(Some(value.expect_expr()?));
}
"minimum_length" => {
def.minimum_length(Some(value.expect_expr()?));
}
"validate" => {
def.validate(Some(value.expect_expr()?));
}

View File

@ -580,32 +580,57 @@ fn handle_struct_named(
})
}
fn named_struct_impl_verify(
span: Span,
fields: &[StructField],
) -> Result<TokenStream, Error> {
fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenStream, Error> {
let mut body = TokenStream::new();
for field in fields {
let field_ident = field.ident;
let field_str = &field.strlit;
if let Some(ref minimum) = field.def.minimum {
body.extend(quote_spanned! { minimum.span() =>
let minimum = #minimum;
if !::proxmox::api::verify::TestMinMax::test_minimum(&self.#field_ident, &minimum) {
if let Some(ref value) = field.def.minimum {
body.extend(quote_spanned! { value.span() =>
let value = #value;
if !::proxmox::api::verify::TestMinMax::test_minimum(&self.#field_ident, &value) {
error_string.push_str(
&format!("field {} out of range, must be >= {}", #field_str, minimum)
&format!("field {} out of range, must be >= {}", #field_str, value)
);
}
});
}
if let Some(ref maximum) = field.def.maximum {
body.extend(quote_spanned! { maximum.span() =>
let maximum = #maximum;
if !::proxmox::api::verify::TestMinMax::test_maximum(&self.#field_ident, &maximum) {
if let Some(ref value) = field.def.maximum {
body.extend(quote_spanned! { value.span() =>
let value = #value;
if !::proxmox::api::verify::TestMinMax::test_maximum(&self.#field_ident, &value) {
error_string.push_str(
&format!("field {} out of range, must be <= {}", #field_str, maximum)
&format!("field {} out of range, must be <= {}", #field_str, value)
);
}
});
}
if let Some(ref value) = field.def.minimum_length {
body.extend(quote_spanned! { value.span() =>
let value = #value;
if !::proxmox::api::verify::TestMinMaxLen::test_minimum_length(
&self.#field_ident,
value,
) {
error_string.push_str(
&format!("field {} too short, must be >= {} characters", #field_str, value)
);
}
});
}
if let Some(ref value) = field.def.maximum_length {
body.extend(quote_spanned! { value.span() =>
let value = #value;
if !::proxmox::api::verify::TestMinMaxLen::test_maximum_length(
&self.#field_ident,
value,
) {
error_string.push_str(
&format!("field {} too long, must be <= {} characters", #field_str, value)
);
}
});

View File

@ -74,3 +74,32 @@ where
self.as_ref().map(|x| *x <= *maximum).unwrap_or(true)
}
}
pub trait TestMinMaxLen<Mark> {
fn test_minimum_length(&self, minimum: usize) -> bool;
fn test_maximum_length(&self, maximum: usize) -> bool;
}
impl<T: AsRef<str>> TestMinMaxLen<mark::Default> for T {
#[inline]
fn test_minimum_length(&self, minimum: usize) -> bool {
self.as_ref().len() >= minimum
}
#[inline]
fn test_maximum_length(&self, maximum: usize) -> bool {
self.as_ref().len() <= maximum
}
}
impl<T: AsRef<str>> TestMinMaxLen<mark::Special> for Option<T> {
#[inline]
fn test_minimum_length(&self, minimum: usize) -> bool {
self.as_ref().map(|x| x.as_ref().len() >= minimum).unwrap_or(true)
}
#[inline]
fn test_maximum_length(&self, maximum: usize) -> bool {
self.as_ref().map(|x| x.as_ref().len() <= maximum).unwrap_or(true)
}
}