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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
#![crate_type = "proc-macro"]
//! A Rust library that allows you to represent the structure as an array.
//! Library works only with named structs whose fields have the same type.
//!
//! # Examples
//! Basic usage:
//! ```
//! use struct_as_array::*;
//!
//! #[derive(AsArray)]
//! struct TestStruct {
//! t1: i32,
//! t2: i32,
//! t3: i32,
//! }
//!
//! let t = TestStruct {
//! t1: 0,
//! t2: 1,
//! t3: 2,
//! };
//!
//! assert_eq!(t.as_array(), [&0, &1, &2]);
//! ```
//!
//! Using as an iterator:
//!
//! ```
//! use struct_as_array::*;
//!
//! #[derive(AsArray)]
//! struct TestStruct {
//! t1: i32,
//! t2: i32,
//! t3: i32,
//! }
//!
//! let t = TestStruct {
//! t1: 0,
//! t2: 1,
//! t3: 2,
//! };
//!
//! for i in t.as_array() {
//! println!("{}", i);
//! }
//! ```
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[proc_macro_derive(AsArray)]
pub fn derive(input: TokenStream) -> TokenStream {
let input: String = input.to_string();
let ast = syn::parse_macro_input(&input).expect("Couldn't parse item");
let result = struct_as_array(ast);
result
.to_string()
.parse()
.expect("couldn't parse string to tokens")
}
fn struct_as_array(ast: syn::MacroInput) -> quote::Tokens {
let name = &ast.ident;
match ast.body {
syn::Body::Struct(syn::VariantData::Struct(ref 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 = field_names.map(|name| quote! { &self.#name });
let n = prefixed_fields.len();
let doc_comment = format!("Represent {} as array.", name);
quote! {
impl #name {
#[doc = #doc_comment]
fn as_array(&self) -> [&#ty_ref; #n] {
[#(prefixed_fields),*]
}
}
}
}
_ => panic!("#[derive(AsArray)] can only be used with structs"),
}
}