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() }