pravega_client_macros/
lib.rs

1//
2// Copyright (c) Dell Inc., or its subsidiaries. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10
11use proc_macro2::TokenStream;
12use quote::{quote, quote_spanned};
13use syn::spanned::Spanned;
14use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics};
15
16/// A derive marco that generates trait impls.
17///
18/// This derive macro implements Fields trait for struct. The Fields trait has a single
19/// method that outputs a list of key value pairs. The key is the struct field name and the value
20/// is the corresponding field value. The value has to be u64 type.
21#[proc_macro_derive(Fields)]
22pub fn derive_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23    // Parse the input tokens into a syntax tree.
24    let input = parse_macro_input!(input as DeriveInput);
25
26    // Used in the quasi-quotation below as `#name`.
27    let name = input.ident;
28
29    // Add a bound `T: Fields` to every type parameter T.
30    let generics = add_trait_bounds(input.generics);
31    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
32
33    // Generate an expression to get the key value pairs of the struct field.
34    let expr = key_value_pairs(&input.data);
35    let expanded = quote! {
36        // The generated impl.
37        impl #impl_generics pravega_client::index::Fields for #name #ty_generics #where_clause {
38            fn get_field_values(&self) -> Vec<(&'static str, u64)> {
39                vec!{#expr}
40            }
41        }
42    };
43
44    // Hand the output tokens back to the compiler.
45    proc_macro::TokenStream::from(expanded)
46}
47
48// Add a bound `T: Fields` to every type parameter T.
49fn add_trait_bounds(mut generics: Generics) -> Generics {
50    for param in &mut generics.params {
51        if let GenericParam::Type(ref mut type_param) = *param {
52            type_param.bounds.push(parse_quote!(Fields));
53        }
54    }
55    generics
56}
57
58fn key_value_pairs(data: &Data) -> TokenStream {
59    match *data {
60        Data::Struct(ref data) => match data.fields {
61            Fields::Named(ref fields) => fields
62                .named
63                .iter()
64                .map(|f| {
65                    let name = f.ident.as_ref().unwrap();
66                    let name_str = format!("{}", name);
67
68                    quote_spanned! {f.span()=>
69                        (#name_str, pravega_client::index::Value::value(&self.#name)),
70                    }
71                })
72                .collect(),
73            Fields::Unnamed(ref _fields) => {
74                quote! {
75                    compile_error!("expected named fields");
76                }
77            }
78            Fields::Unit => {
79                quote! {
80                    compile_error!("expected named fields");
81                }
82            }
83        },
84        Data::Enum(_) | Data::Union(_) => unimplemented!(),
85    }
86}