diff --git a/proxmox-api-macro/src/api/structs.rs b/proxmox-api-macro/src/api/structs.rs index edd1bc11..f9d35be8 100644 --- a/proxmox-api-macro/src/api/structs.rs +++ b/proxmox-api-macro/src/api/structs.rs @@ -168,7 +168,7 @@ fn handle_regular_struct( .as_ref() .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()) } else if let Some(rename_all) = container_attrs.rename_all { 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 { all_of_schemas.extend(quote::quote! {&}); @@ -215,7 +215,7 @@ fn handle_regular_struct( false, Schema::blank(span), ); - handle_regular_field(&mut field_def, field, true)?; + handle_regular_field(&mut field_def, field, true, &attrs)?; if attrs.flatten { all_of_schemas.extend(quote::quote! {&}); @@ -373,6 +373,7 @@ fn handle_regular_field( field_def: &mut ObjectEntry, field: &syn::Field, derived: bool, // whether this field was missing in the schema + attrs: &serde::FieldAttrib, ) -> Result<(), Error> { let schema: &mut Schema = &mut field_def.schema; @@ -389,6 +390,8 @@ fn handle_regular_field( } else if !field_def.optional.expect_bool() { error!(&field.ty => "non-optional Option type?"); } + } else { + attrs.check_non_option_type(); } Ok(()) diff --git a/proxmox-api-macro/src/serde.rs b/proxmox-api-macro/src/serde.rs index d378405b..2821f8d5 100644 --- a/proxmox-api-macro/src/serde.rs +++ b/proxmox-api-macro/src/serde.rs @@ -5,7 +5,9 @@ use std::convert::TryFrom; +use proc_macro2::Span; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::Token; /// Serde name types. @@ -171,6 +173,8 @@ impl TryFrom<&[syn::Attribute]> for ContainerAttrib { pub struct FieldAttrib { pub rename: Option, pub flatten: bool, + has_skip_serializing_if: Option, + has_default: bool, } impl FieldAttrib { @@ -200,11 +204,26 @@ impl FieldAttrib { } else if path.is_ident("flatten") { arg.require_path_only()?; 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(()) } + + 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 {