Add support for serde(skip) in skip_serializing_if

This commit is contained in:
Sosthène Guédon
2025-06-02 10:57:58 +02:00
parent 031c0b2029
commit ac81337193
2 changed files with 43 additions and 19 deletions
+13 -13
View File
@@ -30,6 +30,7 @@ extern crate proc_macro;
mod parse;
use parse::Skip;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{format_ident, quote};
@@ -40,23 +41,20 @@ use crate::parse::Input;
fn serialize_fields(fields: &[parse::Field], offset: usize) -> Vec<proc_macro2::TokenStream> {
fields
.iter()
.map(|field| {
.filter_map(|field| {
let index = field.index + offset;
let member = &field.member;
// println!("field {:?} index {:?}", &field.label, field.index);
match &field.skip_serializing_if {
Some(path) => {
quote! {
if !#path(&self.#member) {
map.serialize_entry(&#index, &self.#member)?;
}
}
}
None => {
quote! {
Skip::If(path) => Some(quote! {
if !#path(&self.#member) {
map.serialize_entry(&#index, &self.#member)?;
}
}
}),
Skip::Always => None,
Skip::None => Some(quote! {
map.serialize_entry(&#index, &self.#member)?;
}),
}
})
.collect()
@@ -69,10 +67,12 @@ fn count_serialized_fields(fields: &[parse::Field]) -> Vec<proc_macro2::TokenStr
// let index = field.index + offset;
let member = &field.member;
match &field.skip_serializing_if {
Some(path) => {
Skip::If(path) => {
quote! { if #path(&self.#member) { 0 } else { 1 } }
}
None => {
Skip::Always => quote! { 0 },
Skip::None => {
quote! { 1 }
}
}
+30 -6
View File
@@ -16,11 +16,23 @@ pub struct StructAttrs {
// pub skip_nones: bool,
}
pub enum Skip {
None,
If(syn::ExprPath),
Always,
}
impl Skip {
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}
pub struct Field {
pub label: String,
pub member: syn::Member,
pub index: usize,
pub skip_serializing_if: Option<syn::ExprPath>,
pub skip_serializing_if: Skip,
}
fn parse_meta(attrs: &mut StructAttrs, meta: ParseNestedMeta) -> Result<()> {
@@ -113,18 +125,30 @@ fn fields_from_ast(
index: i,
// TODO: make this... more concise? handle errors? the thing with the spans?
skip_serializing_if: {
let mut skip_serializing_if = None;
let mut skip_serializing_if = Skip::None;
for attr in &field.attrs {
if attr.path().is_ident("serde") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("skip_serializing_if") {
let litstr: LitStr = meta.value()?.parse()?;
let tokens = syn::parse_str(&litstr.value())?;
if skip_serializing_if.is_some() {
return Err(meta
.error("Multiple attributes for skip_serializing_if"));
if !skip_serializing_if.is_none() {
return Err(meta.error(
"Multiple attributes for skip_serializing_if or skip",
));
}
skip_serializing_if = Some(syn::parse2(tokens)?);
skip_serializing_if = Skip::If(syn::parse2(tokens)?);
Ok(())
} else if meta.path.is_ident("skip") {
if meta.value().is_ok() {
return Err(meta.error("`skip` does not expect any value"));
}
if !skip_serializing_if.is_none() {
return Err(meta.error(
"Multiple attributes for skip_serializing_if or skip",
));
}
skip_serializing_if = Skip::Always;
Ok(())
} else {
Err(meta.error("Unkown field attribute"))