api-macro: make skip_serializing_if without default an error

except for Option types, since this causes deserialization issues

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2023-12-06 14:38:04 +01:00
parent 89b29415a4
commit 2435ab29e2
2 changed files with 25 additions and 3 deletions

View File

@ -168,7 +168,7 @@ fn handle_regular_struct(
.as_ref() .as_ref()
.ok_or_else(|| format_err!(field => "field without name?"))?; .ok_or_else(|| format_err!(field => "field without name?"))?;
if let Some(renamed) = attrs.rename { if let Some(renamed) = attrs.rename.clone() {
(renamed.value(), ident.span()) (renamed.value(), ident.span())
} else if let Some(rename_all) = container_attrs.rename_all { } else if let Some(rename_all) = container_attrs.rename_all {
let name = rename_all.apply_to_field(&ident.to_string()); let name = rename_all.apply_to_field(&ident.to_string());
@ -201,7 +201,7 @@ fn handle_regular_struct(
} }
} }
handle_regular_field(field_def, field, false)?; handle_regular_field(field_def, field, false, &attrs)?;
if attrs.flatten { if attrs.flatten {
all_of_schemas.extend(quote::quote! {&}); all_of_schemas.extend(quote::quote! {&});
@ -215,7 +215,7 @@ fn handle_regular_struct(
false, false,
Schema::blank(span), Schema::blank(span),
); );
handle_regular_field(&mut field_def, field, true)?; handle_regular_field(&mut field_def, field, true, &attrs)?;
if attrs.flatten { if attrs.flatten {
all_of_schemas.extend(quote::quote! {&}); all_of_schemas.extend(quote::quote! {&});
@ -373,6 +373,7 @@ fn handle_regular_field(
field_def: &mut ObjectEntry, field_def: &mut ObjectEntry,
field: &syn::Field, field: &syn::Field,
derived: bool, // whether this field was missing in the schema derived: bool, // whether this field was missing in the schema
attrs: &serde::FieldAttrib,
) -> Result<(), Error> { ) -> Result<(), Error> {
let schema: &mut Schema = &mut field_def.schema; let schema: &mut Schema = &mut field_def.schema;
@ -389,6 +390,8 @@ fn handle_regular_field(
} else if !field_def.optional.expect_bool() { } else if !field_def.optional.expect_bool() {
error!(&field.ty => "non-optional Option type?"); error!(&field.ty => "non-optional Option type?");
} }
} else {
attrs.check_non_option_type();
} }
Ok(()) Ok(())

View File

@ -5,7 +5,9 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use proc_macro2::Span;
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::Token; use syn::Token;
/// Serde name types. /// Serde name types.
@ -171,6 +173,8 @@ impl TryFrom<&[syn::Attribute]> for ContainerAttrib {
pub struct FieldAttrib { pub struct FieldAttrib {
pub rename: Option<syn::LitStr>, pub rename: Option<syn::LitStr>,
pub flatten: bool, pub flatten: bool,
has_skip_serializing_if: Option<Span>,
has_default: bool,
} }
impl FieldAttrib { impl FieldAttrib {
@ -200,11 +204,26 @@ impl FieldAttrib {
} else if path.is_ident("flatten") { } else if path.is_ident("flatten") {
arg.require_path_only()?; arg.require_path_only()?;
self.flatten = true; self.flatten = true;
} else if path.is_ident("skip_serializing_if") {
self.has_skip_serializing_if = Some(match arg.require_name_value() {
Ok(nv) => nv.span(),
Err(_) => arg.span(),
});
} else if path.is_ident("default") {
self.has_default = true;
} }
} }
Ok(()) Ok(())
} }
pub fn check_non_option_type(&self) {
if let Some(span) = self.has_skip_serializing_if {
if !self.has_default {
error!(span, "`skip_serializing_if` without `default`");
}
}
}
} }
impl TryFrom<&[syn::Attribute]> for FieldAttrib { impl TryFrom<&[syn::Attribute]> for FieldAttrib {