macro: support Option in deserialization

When deserializing we currently expect all fields to be
available, but we actually want Option types to be truly
optional...

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-07-19 14:50:23 +02:00
parent ce5fa31721
commit 50eadf23fb
2 changed files with 21 additions and 4 deletions

View File

@ -539,9 +539,10 @@ fn handle_struct_named(
});
field_option_check_or_default_list.extend(quote_spanned! { field_span =>
let #field_ident = #field_ident.ok_or_else(|| {
::serde::de::Error::missing_field(#field_str)
})?;
let #field_ident = ::proxmox::api::ApiType::deserialization_check(
#field_ident,
|| ::serde::de::Error::missing_field(#field_str),
)?;
});
field_name_matches.extend(quote_spanned! { field_span =>

View File

@ -164,7 +164,7 @@ impl dyn ApiMethodInfo + Send + Sync {
/// While this is very useful for structural types, we sometimes to want to be able to pass a
/// simple unconstrainted type like a `String` with no restrictions, so most basic types implement
/// `ApiType` as well.
pub trait ApiType {
pub trait ApiType: Sized {
/// API types need to provide a `TypeInfo`, providing details about the underlying type.
fn type_info() -> &'static TypeInfo;
@ -190,6 +190,14 @@ pub trait ApiType {
fn should_skip_serialization(&self) -> bool {
false
}
#[inline]
fn deserialization_check<F, E>(this: Option<Self>, missing_error: F) -> Result<Self, E>
where
F: FnOnce() -> E,
{
this.ok_or_else(missing_error)
}
}
/// Option types are supposed to wrap their underlying types with an `optional:` text in their
@ -244,6 +252,14 @@ impl<T: ApiType> ApiType for Option<T> {
fn should_skip_serialization(&self) -> bool {
self.is_none()
}
#[inline]
fn deserialization_check<F, E>(this: Option<Self>, _missing_error: F) -> Result<Self, E>
where
F: FnOnce() -> E,
{
Ok(this.unwrap_or(None))
}
}
/// Any `Result<T, Error>` of course gets the same info as `T`, since this only means that it can