api: add helper module for verifiers
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
30105c5743
commit
20b8c709c6
@ -540,12 +540,12 @@ fn handle_struct_named(
|
||||
});
|
||||
}
|
||||
|
||||
let impl_verify = named_struct_impl_verify(item.span(), &fields)?;
|
||||
let impl_serialize =
|
||||
named_struct_derive_serialize(item.span(), type_ident, &type_str, &fields)?;
|
||||
let impl_deserialize =
|
||||
named_struct_derive_deserialize(item.span(), type_ident, &type_str, &fields)?;
|
||||
let accessors =
|
||||
named_struct_impl_accessors(item.span(), type_ident, &fields)?;
|
||||
let accessors = named_struct_impl_accessors(item.span(), type_ident, &fields)?;
|
||||
|
||||
let impl_default = if derive_default {
|
||||
named_struct_impl_default(item.span(), type_ident, &fields)?
|
||||
@ -575,10 +575,58 @@ fn handle_struct_named(
|
||||
&INFO
|
||||
}
|
||||
|
||||
fn verify(&self) -> ::std::result::Result<(), ::failure::Error> {
|
||||
// FIXME: #verifiers
|
||||
Ok(())
|
||||
#impl_verify
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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::Verify::test_minimum(&self.#field_ident, &minimum) {
|
||||
error_string.push_str(
|
||||
&format!("field {} out of range, must be >= {}", #field_str, minimum)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref maximum) = field.def.maximum {
|
||||
body.extend(quote_spanned! { maximum.span() =>
|
||||
let maximum = #maximum;
|
||||
if !::proxmox::api::verify::Verify::test_maximum(&self.#field_ident, &maximum) {
|
||||
error_string.push_str(
|
||||
&format!("field {} out of range, must be <= {}", #field_str, maximum)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !body.is_empty() {
|
||||
body = quote_spanned! { span =>
|
||||
let mut error_string = String::new();
|
||||
#body
|
||||
if !error_string.is_empty() {
|
||||
return Err(::failure::format_err!("{}", error_string));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(quote_spanned! { span =>
|
||||
fn verify(&self) -> ::std::result::Result<(), ::failure::Error> {
|
||||
#body
|
||||
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub use router::*;
|
||||
pub mod cli;
|
||||
|
||||
pub mod meta;
|
||||
pub mod verify;
|
||||
|
||||
/// Return type of an API method.
|
||||
pub type ApiOutput<Body> = Result<Response<Body>, Error>;
|
||||
|
80
proxmox-api/src/verify.rs
Normal file
80
proxmox-api/src/verify.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! Helper module for verifiers implemented via the api macro crate.
|
||||
//!
|
||||
//! We need this to seamlessly support verifying optional types. Consider this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! type Annoying<T> = Option<T>;
|
||||
//!
|
||||
//! #[api({
|
||||
//! fields: {
|
||||
//! foo: {
|
||||
//! description: "Test",
|
||||
//! default: 2,
|
||||
//! minimum: 1,
|
||||
//! maximum: 5,
|
||||
//! },
|
||||
//! bar: {
|
||||
//! description: "Test",
|
||||
//! default: 2,
|
||||
//! minimum: 1,
|
||||
//! maximum: 5,
|
||||
//! },
|
||||
//! },
|
||||
//! })]
|
||||
//! struct Foo {
|
||||
//! foo: Option<usize>,
|
||||
//! bar: Annoying<usize>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The macro does not know that `foo` and `bar` have in fact the same type, and wouldn't know that
|
||||
//! in order to check `bar` it needs to first check the `Option`.
|
||||
//!
|
||||
//! With OIBITs or specialization, we could implement a trait that always gives us "the value we
|
||||
//! actually want to check", but those aren't stable and guarded by a feature gate.
|
||||
//!
|
||||
//! So instead, we implement checks another way.
|
||||
|
||||
pub mod mark {
|
||||
pub struct Default;
|
||||
pub struct Special;
|
||||
}
|
||||
|
||||
pub trait Verify<Other> {
|
||||
fn test_minimum(&self, minimum: &Other) -> bool;
|
||||
fn test_maximum(&self, maximum: &Other) -> bool;
|
||||
}
|
||||
|
||||
impl<Other> Verify<Other> for Other
|
||||
where
|
||||
Other: Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn test_minimum(&self, minimum: &Other) -> bool {
|
||||
*self >= *minimum
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn test_maximum(&self, maximum: &Other) -> bool {
|
||||
*self <= *maximum
|
||||
}
|
||||
}
|
||||
|
||||
impl<Other> Verify<Other> for Option<Other>
|
||||
where
|
||||
Other: Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn test_minimum(&self, minimum: &Other) -> bool {
|
||||
self.as_ref().map(|x| *x >= *minimum).unwrap_or(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn test_maximum(&self, maximum: &Other) -> bool {
|
||||
self.as_ref().map(|x| *x <= *maximum).unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_minimum<U, T: Verify<U>>(value: &T, minimum: &U) -> bool {
|
||||
value.test_minimum(minimum)
|
||||
}
|
Loading…
Reference in New Issue
Block a user