prpc_serde_bytes/
lib.rs

1extern crate proc_macro;
2
3use syn::{parse_macro_input, punctuated::Punctuated, ItemStruct, Meta, Token};
4
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7
8#[proc_macro_attribute]
9pub fn prpc_serde_bytes(_attr: TokenStream, item: TokenStream) -> TokenStream {
10    patch(parse_macro_input!(item)).into()
11}
12
13pub(crate) fn patch(item: TokenStream2) -> TokenStream2 {
14    match patch_or_err(item) {
15        Ok(tokens) => tokens,
16        Err(err) => err.to_compile_error(),
17    }
18}
19
20fn has_attr(attrs: &[syn::Attribute], name: &str, field: &str) -> bool {
21    for attr in attrs.iter() {
22        if attr.path().is_ident(name) {
23            let Ok(nested) = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
24            else {
25                continue;
26            };
27            for meta in nested.iter() {
28                if meta.path().is_ident(field) {
29                    return true;
30                }
31            }
32        }
33    }
34    false
35}
36
37fn patch_or_err(input: TokenStream2) -> syn::Result<TokenStream2> {
38    let Ok(mut input) = syn::parse2::<ItemStruct>(input.clone()) else {
39        return Ok(input);
40    };
41    for field in input.fields.iter_mut() {
42        if !has_attr(&field.attrs, "serde", "with") && has_attr(&field.attrs, "prost", "bytes") {
43            if has_attr(&field.attrs, "prost", "optional") {
44                field
45                    .attrs
46                    .push(syn::parse_quote!(#[serde(with = "::prpc::serde_helpers::option_bytes_as_hex_str")]));
47            } else if has_attr(&field.attrs, "prost", "repeated") {
48                field
49                    .attrs
50                    .push(syn::parse_quote!(#[serde(with = "::prpc::serde_helpers::vec_bytes_as_hex_str")]));
51            } else {
52                field.attrs.push(
53                    syn::parse_quote!(#[serde(with = "::prpc::serde_helpers::bytes_as_hex_str")]),
54                );
55            }
56        }
57        if !has_attr(&field.attrs, "serde", "default")
58            && has_attr(&field.attrs, "prost", "repeated")
59        {
60            field.attrs.push(syn::parse_quote!(#[serde(default)]));
61        }
62    }
63
64    Ok(syn::parse_quote! {
65        #input
66    })
67}
68
69#[cfg(test)]
70mod tests;