forked from Proxmox/proxmox
macro: support serialize_with and deserialize_with
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
a6fcaf27e5
commit
d5ee03da9f
@ -1,6 +1,6 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use failure::{bail, Error};
|
use failure::{bail, Error};
|
||||||
@ -107,6 +107,11 @@ pub struct ParameterDefinition {
|
|||||||
/// the format name.
|
/// the format name.
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
pub format: Option<syn::Path>,
|
pub format: Option<syn::Path>,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
pub serialize_with: Option<syn::Path>,
|
||||||
|
#[builder(default)]
|
||||||
|
pub deserialize_with: Option<syn::Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParameterDefinition {
|
impl ParameterDefinition {
|
||||||
@ -144,6 +149,26 @@ impl ParameterDefinition {
|
|||||||
"format" => {
|
"format" => {
|
||||||
def.format(Some(value.expect_path()?));
|
def.format(Some(value.expect_path()?));
|
||||||
}
|
}
|
||||||
|
"serialize_with" => {
|
||||||
|
def.serialize_with(Some(value.expect_path()?));
|
||||||
|
}
|
||||||
|
"deserialize_with" => {
|
||||||
|
def.deserialize_with(Some(value.expect_path()?));
|
||||||
|
}
|
||||||
|
"serialization" => {
|
||||||
|
let mut de = value.expect_path()?;
|
||||||
|
let mut ser = de.clone();
|
||||||
|
ser.segments.push(syn::PathSegment {
|
||||||
|
ident: Ident::new("serialize", obj_span),
|
||||||
|
arguments: syn::PathArguments::None,
|
||||||
|
});
|
||||||
|
de.segments.push(syn::PathSegment {
|
||||||
|
ident: Ident::new("deserialize", obj_span),
|
||||||
|
arguments: syn::PathArguments::None,
|
||||||
|
});
|
||||||
|
def.deserialize_with(Some(de));
|
||||||
|
def.serialize_with(Some(ser));
|
||||||
|
}
|
||||||
other => c_bail!(key.span(), "invalid key in type definition: {}", other),
|
other => c_bail!(key.span(), "invalid key in type definition: {}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,6 +666,31 @@ fn named_struct_impl_verify(span: Span, fields: &[StructField]) -> Result<TokenS
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap_serialize_with(
|
||||||
|
span: Span,
|
||||||
|
name: &Ident,
|
||||||
|
ty: &syn::Type,
|
||||||
|
with: &syn::Path,
|
||||||
|
) -> (TokenStream, Ident) {
|
||||||
|
let helper_name = Ident::new(
|
||||||
|
&format!("SerializeWith{}", crate::util::to_camel_case(&name.to_string())),
|
||||||
|
name.span(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(quote_spanned! { span =>
|
||||||
|
struct #helper_name<'a>(&'a #ty);
|
||||||
|
|
||||||
|
impl<'a> ::serde::ser::Serialize for #helper_name<'a> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: ::serde::ser::Serializer,
|
||||||
|
{
|
||||||
|
#with(self.0, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, helper_name)
|
||||||
|
}
|
||||||
|
|
||||||
fn named_struct_derive_serialize(
|
fn named_struct_derive_serialize(
|
||||||
span: Span,
|
span: Span,
|
||||||
type_ident: &Ident,
|
type_ident: &Ident,
|
||||||
@ -679,11 +704,27 @@ fn named_struct_derive_serialize(
|
|||||||
let field_ident = field.ident;
|
let field_ident = field.ident;
|
||||||
let field_span = field.ident.span();
|
let field_span = field.ident.span();
|
||||||
let field_str = &field.strlit;
|
let field_str = &field.strlit;
|
||||||
entries.extend(quote_spanned! { field_span =>
|
match field.def.serialize_with.as_ref() {
|
||||||
if !::proxmox::api::ApiType::should_skip_serialization(&self.#field_ident) {
|
Some(path) => {
|
||||||
state.serialize_field(#field_str, &self.#field_ident)?;
|
let (serializer, serializer_name) =
|
||||||
|
wrap_serialize_with(field_span, field_ident, &field.ty, path);
|
||||||
|
|
||||||
|
entries.extend(quote_spanned! { field_span =>
|
||||||
|
if !::proxmox::api::ApiType::should_skip_serialization(&self.#field_ident) {
|
||||||
|
#serializer
|
||||||
|
|
||||||
|
state.serialize_field(#field_str, &#serializer_name(&self.#field_ident))?;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
None => {
|
||||||
|
entries.extend(quote_spanned! { field_span =>
|
||||||
|
if !::proxmox::api::ApiType::should_skip_serialization(&self.#field_ident) {
|
||||||
|
state.serialize_field(#field_str, &self.#field_ident)?;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(quote_spanned! { span =>
|
Ok(quote_spanned! { span =>
|
||||||
@ -701,6 +742,37 @@ fn named_struct_derive_serialize(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap_deserialize_with(
|
||||||
|
span: Span,
|
||||||
|
name: &Ident,
|
||||||
|
ty: &syn::Type,
|
||||||
|
with: &syn::Path,
|
||||||
|
) -> (TokenStream, Ident) {
|
||||||
|
let helper_name = Ident::new(
|
||||||
|
&format!("DeserializeWith{}", crate::util::to_camel_case(&name.to_string())),
|
||||||
|
name.span(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(quote_spanned! { span =>
|
||||||
|
struct #helper_name<'de> {
|
||||||
|
value: #ty,
|
||||||
|
_lifetime: ::std::marker::PhantomData<&'de ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> ::serde::de::Deserialize<'de> for #helper_name<'de> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: ::serde::de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
value: #with(deserializer)?,
|
||||||
|
_lifetime: ::std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, helper_name)
|
||||||
|
}
|
||||||
|
|
||||||
fn named_struct_derive_deserialize(
|
fn named_struct_derive_deserialize(
|
||||||
span: Span,
|
span: Span,
|
||||||
type_ident: &Ident,
|
type_ident: &Ident,
|
||||||
@ -740,18 +812,42 @@ fn named_struct_derive_deserialize(
|
|||||||
)?;
|
)?;
|
||||||
});
|
});
|
||||||
|
|
||||||
field_option_init_list.extend(quote_spanned! { field_span =>
|
match field.def.deserialize_with.as_ref() {
|
||||||
let mut #field_ident = None;
|
Some(path) => {
|
||||||
});
|
let (deserializer, deserializer_name) =
|
||||||
|
wrap_deserialize_with(field_span, field_ident, &field.ty, path);
|
||||||
|
|
||||||
field_value_matches.extend(quote_spanned! { field_span =>
|
field_option_init_list.extend(quote_spanned! { field_span =>
|
||||||
Field(#mem_id) => {
|
#deserializer
|
||||||
if #field_ident.is_some() {
|
|
||||||
return Err(::serde::de::Error::duplicate_field(#field_str));
|
let mut #field_ident = None;
|
||||||
}
|
});
|
||||||
#field_ident = Some(_api_macro_map_.next_value()?);
|
|
||||||
|
field_value_matches.extend(quote_spanned! { field_span =>
|
||||||
|
Field(#mem_id) => {
|
||||||
|
if #field_ident.is_some() {
|
||||||
|
return Err(::serde::de::Error::duplicate_field(#field_str));
|
||||||
|
}
|
||||||
|
let tmp: #deserializer_name = _api_macro_map_.next_value()?;
|
||||||
|
#field_ident = Some(tmp.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
None => {
|
||||||
|
field_option_init_list.extend(quote_spanned! { field_span =>
|
||||||
|
let mut #field_ident = None;
|
||||||
|
});
|
||||||
|
|
||||||
|
field_value_matches.extend(quote_spanned! { field_span =>
|
||||||
|
Field(#mem_id) => {
|
||||||
|
if #field_ident.is_some() {
|
||||||
|
return Err(::serde::de::Error::duplicate_field(#field_str));
|
||||||
|
}
|
||||||
|
#field_ident = Some(_api_macro_map_.next_value()?);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(quote_spanned! { span =>
|
Ok(quote_spanned! { span =>
|
||||||
|
Loading…
Reference in New Issue
Block a user