Updater: take serde renames into account

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2021-02-24 09:21:52 +01:00
parent d3636d45d9
commit 857b8ab2b9
2 changed files with 32 additions and 18 deletions

View File

@ -1,7 +1,10 @@
use std::convert::TryFrom;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use crate::serde;
use crate::util;
pub(crate) fn updatable(item: TokenStream) -> Result<TokenStream, syn::Error> {
@ -90,6 +93,7 @@ fn derive_named_struct_updatable(
) -> Result<TokenStream, syn::Error> {
no_generics(generics);
let serde_container_attrs = serde::ContainerAttrib::try_from(&attrs[..])?;
let args = UpdatableArgs::from_attributes(attrs);
let updater = match args.updater {
Some(updater) => updater,
@ -101,32 +105,41 @@ fn derive_named_struct_updatable(
let mut build = TokenStream::new();
for field in fields.named {
let serde_attrs = serde::SerdeAttrib::try_from(&field.attrs[..])?;
let attrs = UpdaterFieldArgs::from_attributes(field.attrs);
let field_name = field
let field_ident = field
.ident
.as_ref()
.expect("unnamed field in named struct?");
let field_name_string = field_name.to_string();
let field_name_string = if let Some(renamed) = serde_attrs.rename {
renamed.into_str()
} else if let Some(rename_all) = serde_container_attrs.rename_all {
let name = rename_all.apply_to_field(&field_ident.to_string());
name
} else {
field_ident.to_string()
};
let build_err = format!(
"failed to build value for field '{}': {{}}",
field_name_string
);
if util::is_option_type(&field.ty).is_some() {
delete.extend(quote! {
#field_name_string => { self.#field_name = None; }
#field_name_string => { self.#field_ident = None; }
});
build.extend(quote! {
#field_name: ::proxmox::api::schema::Updatable::try_build_from(
from.#field_name
#field_ident: ::proxmox::api::schema::Updatable::try_build_from(
from.#field_ident
)
.map_err(|err| ::anyhow::format_err!(#build_err, err))?,
});
} else {
build.extend(quote! {
#field_name: ::proxmox::api::schema::Updatable::try_build_from(
from.#field_name
#field_ident: ::proxmox::api::schema::Updatable::try_build_from(
from.#field_ident
)
.map_err(|err| ::anyhow::format_err!(#build_err, err))?,
});
@ -135,18 +148,18 @@ fn derive_named_struct_updatable(
if attrs.fixed {
let error = format!(
"field '{}' must not be set when updating existing data",
field_name
field_ident
);
apply.extend(quote! {
if from.#field_name.is_some() {
if from.#field_ident.is_some() {
::anyhow::bail!(#error);
}
});
} else {
apply.extend(quote! {
::proxmox::api::schema::Updatable::update_from(
&mut self.#field_name,
from.#field_name,
&mut self.#field_ident,
from.#field_ident,
delete,
)?;
});

View File

@ -9,9 +9,10 @@ use proxmox::api::schema::{Updatable, Updater};
/// An example of a simple struct type.
#[cfg_attr(not(feature = "noserde"), derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Updater)]
#[serde(rename_all = "kebab-case")]
pub struct Simple {
/// A test string.
one: String,
one_field: String,
/// An optional auto-derived value for testing:
#[serde(skip_serializing_if = "Option::is_empty")]
@ -191,7 +192,7 @@ mod test_creatable {
"id": "Id1",
"name": "The Name",
"extra": "Extra Info",
"one": "Part of Simple",
"one-field": "Part of Simple",
"info2": "More Info 2",
}),
&API_METHOD_CREATE_THING,
@ -209,7 +210,7 @@ mod test_creatable {
complex: Complex {
extra: "Extra Info".to_string(),
simple: Simple {
one: "Part of Simple".to_string(),
one_field: "Part of Simple".to_string(),
opt: None,
},
},
@ -245,7 +246,7 @@ mod test_creatable {
complex: Complex {
extra: "Extra Info".to_string(),
simple: Simple {
one: "Part of Simple".to_string(),
one_field: "Part of Simple".to_string(),
opt: None,
},
},
@ -270,7 +271,7 @@ mod test_creatable {
complex: Complex {
extra: "Partial flatten update".to_string(),
simple: Simple {
one: "Part of Simple".to_string(),
one_field: "Part of Simple".to_string(),
opt: None,
},
},
@ -295,7 +296,7 @@ mod test_creatable {
complex: Complex {
extra: "Partial flatten update".to_string(),
simple: Simple {
one: "Part of Simple".to_string(),
one_field: "Part of Simple".to_string(),
opt: Some("Deeply nested optional update.".to_string()),
},
},
@ -320,7 +321,7 @@ mod test_creatable {
complex: Complex {
extra: "Partial flatten update".to_string(),
simple: Simple {
one: "Part of Simple".to_string(),
one_field: "Part of Simple".to_string(),
opt: None,
},
},