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
//
// Copyright (c) Dell Inc., or its subsidiaries. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics};
/// A derive marco that generates trait impls.
///
/// This derive macro implements Fields trait for struct. The Fields trait has a single
/// method that outputs a list of key value pairs. The key is the struct field name and the value
/// is the corresponding field value. The value has to be u64 type.
#[proc_macro_derive(Fields)]
pub fn derive_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse the input tokens into a syntax tree.
let input = parse_macro_input!(input as DeriveInput);
// Used in the quasi-quotation below as `#name`.
let name = input.ident;
// Add a bound `T: Fields` to every type parameter T.
let generics = add_trait_bounds(input.generics);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// Generate an expression to get the key value pairs of the struct field.
let expr = key_value_pairs(&input.data);
let expanded = quote! {
// The generated impl.
impl #impl_generics pravega_client::index::Fields for #name #ty_generics #where_clause {
fn get_field_values(&self) -> Vec<(&'static str, u64)> {
vec!{#expr}
}
}
};
// Hand the output tokens back to the compiler.
proc_macro::TokenStream::from(expanded)
}
// Add a bound `T: Fields` to every type parameter T.
fn add_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push(parse_quote!(Fields));
}
}
generics
}
fn key_value_pairs(data: &Data) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => fields
.named
.iter()
.map(|f| {
let name = f.ident.as_ref().unwrap();
let name_str = format!("{}", name);
quote_spanned! {f.span()=>
(#name_str, pravega_client::index::Value::value(&self.#name)),
}
})
.collect(),
Fields::Unnamed(ref _fields) => {
quote! {
compile_error!("expected named fields");
}
}
Fields::Unit => {
quote! {
compile_error!("expected named fields");
}
}
},
Data::Enum(_) | Data::Union(_) => unimplemented!(),
}
}