cdr_encoding_size_derive/
lib.rs

1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{
4  parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam,
5  Generics,
6};
7
8// This is derived form the HeapSize example in syn crate documentation.
9// the main difference are
10// * This works on maximum size of data types, whereas the original example
11//   counts data item sizes.
12// * different naming
13
14/// Derive macro for implementing CdrEncodingSize trait.
15#[proc_macro_derive(CdrEncodingSize)]
16pub fn derive_cdr_encoding_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
17  // Parse the input tokens into a syntax tree.
18  let input = parse_macro_input!(input as DeriveInput);
19
20  // Used in the quasi-quotation below as `#name`.
21  let name = input.ident;
22
23  // Add a bound `T: CdrEncodingSize` to every type parameter T.
24  let generics = add_trait_bounds(input.generics);
25  let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
26
27  // Generate an expression to sum up the size of each field.
28  let sum = cdr_size_sum(&input.data);
29
30  let expanded = quote! {
31      // The generated impl.
32      impl #impl_generics cdr_encoding_size::CdrEncodingSize for #name #ty_generics #where_clause {
33          fn cdr_encoding_max_size() -> cdr_encoding_size::CdrEncodingMaxSize {
34              #sum
35          }
36      }
37  };
38
39  // Hand the output tokens back to the compiler.
40  proc_macro::TokenStream::from(expanded)
41}
42
43// Add a bound `T: CdrEncodingSize` to every type parameter T.
44fn add_trait_bounds(mut generics: Generics) -> Generics {
45  for param in &mut generics.params {
46    if let GenericParam::Type(ref mut type_param) = *param {
47      type_param
48        .bounds
49        .push(parse_quote!(cdr_encoding_size::CdrEncodingSize));
50    }
51  }
52  generics
53}
54
55// Generate an expression to sum up the size of each field.
56fn cdr_size_sum(data: &Data) -> TokenStream {
57  match *data {
58    Data::Struct(ref data) => {
59      match data.fields {
60        Fields::Named(ref fields) => {
61          let recurse = fields.named.iter().map(|f| {
62            let ty = &f.ty;
63            quote_spanned! {f.span()=>
64                <#ty>::cdr_encoding_max_size()
65            }
66          });
67          quote! {
68              cdr_encoding_size::CdrEncodingMaxSize::Bytes(0) #(+ #recurse)*
69          }
70        }
71        Fields::Unnamed(ref fields) => {
72          let recurse = fields.unnamed.iter().enumerate().map(|(_i, f)| {
73            let ty = &f.ty;
74            //let index = Index::from(i);
75            quote_spanned! {f.span()=>
76                <#ty>::cdr_encoding_max_size()
77            }
78          });
79          quote! {
80              cdr_encoding_size::CdrEncodingMaxSize::Bytes(0) #(+ #recurse)*
81          }
82        }
83        Fields::Unit => {
84          quote!(cdr_encoding_size::CdrEncodingMaxSize::Bytes(0))
85        }
86      }
87    }
88    Data::Enum(_) | Data::Union(_) => unimplemented!(),
89  }
90}