diff --git a/src/lib.rs b/src/lib.rs index 2c685d5..30cd83a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { 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 { + Skip::If(path) => { quote! { if #path(&self.#member) { 0 } else { 1 } } } - None => { + Skip::Always => quote! { 0 }, + + Skip::None => { quote! { 1 } } } diff --git a/src/parse.rs b/src/parse.rs index 3255dc4..c5e1490 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -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, + 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"))