#![crate_type = "proc-macro"]
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;
use syn::DeriveInput;
#[proc_macro_derive(AsArray)]
pub fn as_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
proc_macro::TokenStream::from(struct_as_array(ast))
}
fn struct_as_array(ast: syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
match ast.data {
syn::Data::Struct(data_struct) => {
let fields = &data_struct.fields;
let mut types = Vec::new();
for field in fields {
let ty = &field.ty;
types.push(ty.clone());
}
let ty_ref = types.pop();
match ty_ref.clone() {
Some(ty_ref) => {
for ty in types {
if ty != ty_ref {
panic!("Fields in struct {} have not same types", name)
}
}
}
None => panic!("Struct {} have no any fields", name),
}
let field_names = fields.iter().map(|f| {
let f_name = &f.ident;
quote!(#f_name)
});
let prefixed_fields_ref = field_names.clone().map(|name| quote! { &self.#name });
let prefixed_fields = field_names.map(|name| quote! { #name });
let prefixed_fields_clone = prefixed_fields.clone();
let n = prefixed_fields.len();
let doc_comment_ref = format!("Represent {} as array.", name);
let doc_comment = format!("Convert {} to array.", name);
quote! {
impl #name {
#[doc = #doc_comment_ref]
fn as_array(&self) -> [&#ty_ref; #n] {
[#(#prefixed_fields_ref),*]
}
#[doc = #doc_comment]
fn to_array(self) -> [#ty_ref; #n] {
let #name {#(#prefixed_fields),*} = self;
[#(#prefixed_fields_clone),*]
}
}
}
}
_ => panic!("#[derive(AsArray)] can only be used with structs"),
}
}