1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use inflector::cases::snakecase::to_snake_case;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Index};
use utils::{Item, ItemInfo, ItemType};

mod utils;

#[proc_macro_derive(Maskable, attributes(fieldmask))]
pub fn derive_maskable(input: TokenStream) -> TokenStream {
    let input: Item = parse_macro_input!(input);
    let ItemInfo {
        item_type,
        ident,
        generics,
        fields,
    } = input.get_info();

    let (impl_generics, ty_generics, where_clauses) = generics.split_for_impl();
    let field_indices = fields.iter().enumerate().map(|(i, _field)| Index::from(i));
    let field_idents = fields.iter().map(|field| &field.ident).collect::<Vec<_>>();
    let field_types = fields.iter().map(|f| f.ty);
    let match_arms = fields.iter().enumerate().map(|(i, field)| {
        let index = Index::from(i);
        if field.is_flatten {
            quote! {
                _ if mask
                    .0
                    .#index
                    .try_bitor_assign(field_mask_segs)
                    .map(|_| true)
                    .or_else(|l| if l.depth == 0 { Ok(false) } else { Err(l) })? =>
                {
                    ()
                }
            }
        } else {
            let prefix = to_snake_case(&field.ident.to_string());
            quote! {
                [#prefix, tail @ ..] => mask.0.#index.try_bitor_assign(tail).map_err(|mut e| {
                    e.depth += 1;
                    e
                })?,
            }
        }
    });
    let match_arm_groups = fields.iter().map(|target_field| {
        let target_ident = target_field.ident;
        let arms = fields.iter().enumerate().map(|(i, src_field)| {
            let index = Index::from(i);
            let src_ident = src_field.ident;
            if src_ident == target_ident {
                quote! {
                    Self::#src_ident(s) if mask.0.#index != ::fieldmask::FieldMask::default() => {
                        mask.0.#index.apply(t, s);
                    }
                }
            } else {
                let src_ty = src_field.ty;
                quote! {
                    Self::#src_ident(s) if mask.0.#index != ::fieldmask::FieldMask::default() => {
                        let mut new = <#src_ty>::default();
                        mask.0.#index.apply(&mut new, s);
                        *self = Self::#src_ident(new);
                    }
                }
            }
        });
        quote! {
            Self::#target_ident(t) => match src {
                #(#arms)*
                _ => return false,
            }
        }
    });

    let additional_impl = match item_type {
        ItemType::Enum => quote! {
            impl#impl_generics ::fieldmask::OptionMaskable for #ident#ty_generics
            #where_clauses
            {
                fn apply_mask(&mut self, src: Self, mask: Self::Mask) -> bool {
                    match self {
                        #(#match_arm_groups)*
                    }
                    return true;
                }
            }
        },
        ItemType::Struct => quote! {
            impl#impl_generics ::fieldmask::SelfMaskable for #ident#ty_generics
            #where_clauses
            {
                fn apply_mask(&mut self, src: Self, mask: Self::Mask) {
                    #(mask.0.#field_indices.apply(&mut self.#field_idents, src.#field_idents);)*
                }
            }
        },
    };

    (quote! {
        impl#impl_generics ::fieldmask::Maskable for #ident#ty_generics
        #where_clauses
        {
            type Mask = ::fieldmask::BitwiseWrap<(#(::fieldmask::FieldMask<#field_types>,)*)>;

            fn try_bitor_assign_mask(
                mask: &mut Self::Mask,
                field_mask_segs: &[&::core::primitive::str],
            ) -> ::core::result::Result<(), ::fieldmask::DeserializeMaskError> {
                match field_mask_segs {
                    [] => *mask = !Self::Mask::default(),
                    #(#match_arms)*
                    _ => return ::core::result::Result::Err(::fieldmask::DeserializeMaskError{
                        type_str: stringify!(#ident),
                        field: field_mask_segs[0].into(),
                        depth: 0,
                    }),
                }
                Ok(())
            }
        }

        #additional_impl
    })
    .into()
}