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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use proc_macro::{Span, TokenStream};
use quote::ToTokens;
use serde_derive_internals::{
    ast::{Container, Data, Style},
    Ctxt, Derive,
};
use syn::{DeriveInput, Error};

#[proc_macro_derive(SerializePartial, attributes(serde))]
pub fn serialize_partial(input: TokenStream) -> TokenStream {
    let cx = Ctxt::new();
    let item = syn::parse_macro_input!(input as DeriveInput);
    let Container {
        data,
        attrs,
        ident,
        original,
        ..
    } = match Container::from_ast(&cx, &item, Derive::Serialize) {
        Some(c) => c,
        None => return item.to_token_stream().into(),
    };
    let ident = &ident;
    let vis = &original.vis;

    if cx.check().is_err() {
        return item.to_token_stream().into();
    }

    let mut fields = match data {
        Data::Struct(Style::Struct, f) => f,
        _ => {
            return Error::new(
                Span::call_site().into(),
                "SerializePartial only supports structs",
            )
            .to_compile_error()
            .into()
        }
    };
    for f in fields.iter_mut() {
        f.attrs.rename_by_rules(attrs.rename_all_rules());
    }

    let fields_struct_ident = &quote::format_ident!("{}Fields", ident);
    let fields_struct_idents = fields.iter().map(|f| f.original.ident.clone().unwrap());
    let fields_struct_idents_impl = fields_struct_idents.clone();
    let fields_struct_idents_iter = fields_struct_idents.clone();
    let fields_len = fields.len();
    let fields_struct_names = fields.iter().map(|f| f.attrs.name().serialize_name());

    let filter_struct_ident = &quote::format_ident!("{}Filter", ident);
    let filter_struct_idents = fields_struct_idents.clone();
    let filter_struct_idents_impl = filter_struct_idents.clone();
    let filter_struct_names = fields_struct_names.clone();

    let trait_impl_idents = filter_struct_idents.clone();
    let trait_impl_names = filter_struct_names.clone();

    let fields_struct = quote::quote! {
        #[derive(Debug, Clone, Copy)]
        #vis struct #fields_struct_ident {
            #(
                pub #fields_struct_idents: ::serde_partial::Field<#ident>,
            )*
        }

        impl #fields_struct_ident {
            pub const FIELDS: Self = Self {
                #(
                    #fields_struct_idents_impl: ::serde_partial::Field::new(#fields_struct_names),
                )*
            };
        }

        impl ::core::iter::IntoIterator for #fields_struct_ident {
            type Item = ::serde_partial::Field<#ident>;
            type IntoIter = ::core::array::IntoIter<Self::Item, #fields_len>;

            fn into_iter(self) -> Self::IntoIter {
                #[allow(deprecated)]
                ::core::array::IntoIter::new([
                    #(
                        self.#fields_struct_idents_iter,
                    )*
                ])
            }
        }
    };

    let filter_struct = quote::quote! {
        #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
        #vis struct #filter_struct_ident {
            #(
                #filter_struct_idents: bool,
            )*
        }

        impl ::serde_partial::SerializeFilter<#ident> for #filter_struct_ident {
            fn skip(&self, field: ::serde_partial::Field<#ident>) -> bool {
                match field.name() {
                    #(
                        #filter_struct_names => !self.#filter_struct_idents_impl,
                    )*
                    _ => panic!("unknown field"),
                }
            }
        }
    };

    let trait_impl = quote::quote! {
        impl ::serde_partial::SerializePartial for #ident {
            type Fields = #fields_struct_ident;
            type Filter = #filter_struct_ident;

            fn with_fields<F, I>(&self, select: F) -> ::serde_partial::Partial<'_, Self>
            where
                F: ::core::ops::FnOnce(Self::Fields) -> I,
                I: ::core::iter::IntoIterator<Item = ::serde_partial::Field<Self>>,
            {
                let fields = Self::Fields::FIELDS;
                let mut filter = <Self::Filter as ::core::default::Default>::default();

                for filtered in select(fields) {
                    match filtered.name() {
                        #(
                            #trait_impl_names => { filter.#trait_impl_idents = true }
                        )*
                        _ => panic!("unknown field"),
                    }
                }

                ::serde_partial::Partial {
                    value: self,
                    filter,
                }
            }
        }
    };

    let derive = quote::quote! {
        #[doc(hidden)]
        #[allow(non_upper_case_globals, non_camel_case_types)]
        const _: () = {
            #fields_struct
            #filter_struct
            #trait_impl
        };
    };
    derive.into()
}