oasis_borsh_derive/
lib.rs

1extern crate proc_macro;
2
3use oasis_borsh_derive_internal::*;
4use proc_macro::TokenStream;
5use quote::{quote, format_ident};
6use syn::{parse_macro_input, Ident, ItemEnum, ItemStruct, ItemUnion, LitInt, Token};
7
8#[proc_macro_derive(BorshSerialize, attributes(borsh_skip))]
9pub fn borsh_serialize(input: TokenStream) -> TokenStream {
10    let res = if let Ok(input) = syn::parse::<ItemStruct>(input.clone()) {
11        struct_ser(&input)
12    } else if let Ok(input) = syn::parse::<ItemEnum>(input.clone()) {
13        enum_ser(&input)
14    } else if let Ok(input) = syn::parse::<ItemUnion>(input.clone()) {
15        union_ser(&input)
16    } else {
17        // Derive macros can only be defined on structs, enums, and unions.
18        unreachable!()
19    };
20    TokenStream::from(match res {
21        Ok(res) => res,
22        Err(err) => err.to_compile_error(),
23    })
24}
25
26#[proc_macro_derive(BorshDeserialize, attributes(borsh_skip, borsh_init))]
27pub fn borsh_deserialize(input: TokenStream) -> TokenStream {
28    let res = if let Ok(input) = syn::parse::<ItemStruct>(input.clone()) {
29        struct_de(&input)
30    } else if let Ok(input) = syn::parse::<ItemEnum>(input.clone()) {
31        enum_de(&input)
32    } else if let Ok(input) = syn::parse::<ItemUnion>(input.clone()) {
33        union_de(&input)
34    } else {
35        // Derive macros can only be defined on structs, enums, and unions.
36        unreachable!()
37    };
38    TokenStream::from(match res {
39        Ok(res) => res,
40        Err(err) => err.to_compile_error(),
41    })
42}
43
44struct SeqMacroSpec {
45    mac_ident: Ident,
46    prefix: Option<Ident>,
47    lengths: Vec<usize>,
48}
49
50impl syn::parse::Parse for SeqMacroSpec {
51    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
52        let mac_ident = input.parse()?;
53        input.parse::<Token![=>]>()?;
54
55        let prefix = input.parse::<Option<Ident>>()?;
56        if prefix.is_some() {
57            input.parse::<Token![::]>()?;
58        }
59
60        let seq_lengths;
61        syn::parenthesized!(seq_lengths in input);
62        let punctuated_lengths: syn::punctuated::Punctuated<LitInt, syn::parse::Nothing> =
63            seq_lengths.parse_terminated(LitInt::parse)?;
64        let lengths = punctuated_lengths
65            .iter()
66            .map(|l| l.base10_parse::<usize>())
67            .collect::<Result<Vec<_>, _>>()?;
68
69        Ok(SeqMacroSpec {
70            mac_ident,
71            prefix,
72            lengths,
73        })
74    }
75}
76
77#[proc_macro]
78#[doc(hidden)]
79pub fn _gen_seq_macro(input: TokenStream) -> TokenStream {
80    let SeqMacroSpec {
81        mac_ident,
82        prefix,
83        lengths,
84    } = parse_macro_input!(input as SeqMacroSpec);
85    let seqs = lengths.iter().copied().map(|len| {
86        (0..len)
87            .map(|i| {
88                if let Some(prefix) = &prefix {
89                    let seq_ident = format_ident!("{}{}", prefix, i);
90                    quote!(#seq_ident)
91                } else {
92                    let seq_lit = proc_macro2::Literal::usize_unsuffixed(i);
93                    quote!(#seq_lit)
94                }
95            })
96            .collect::<Vec<_>>()
97    });
98    let length_lits = lengths
99        .iter()
100        .map(|&i| proc_macro2::Literal::usize_unsuffixed(i));
101    let ts = TokenStream::from(quote! {
102        #mac_ident!(#(#length_lits => ( #(#seqs)* ))*);
103    });
104    ts
105}