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
#![warn(missing_docs)] //! # struct_gen_derive //! struct_gen_derive is a dependency of struct_gen that implements the `Zero` trait for fixed-length arrays over an enum of types //! Out of context of struct_gen, it is pretty useless. extern crate proc_macro; extern crate syn; #[macro_use] extern crate quote; use proc_macro::TokenStream; use syn::Ty::Path; use syn::{Body, VariantData}; /// struct_iterator is a procedural macro for taking a tuple struct of types /// implementing`Zero` on each type for fixed-size arrays of lengths [0,10]. /// /// # struct_iterator /// Comments and boilerplate from [dtolnay's presentation at Mozilla][dtolnay], /// basic struct iteration based off of [Christopher Breeden's blog post][blog]. /// /// [dtolnay]: https://air.mozilla.org/rust-meetup-december-2016-12-15/ /// [blog]: https://cbreeden.github.io/Macros11/ /// /// *Note:* for the internal `impl_struct_iter` function we use default for now instead /// of zoor. This will likely change by 0.2.0 release of struct_gen, however for now, where /// we are only interested in the zero in zero-or-override, this is fine. #[proc_macro_derive(StructIterator)] pub fn struct_iterator(input: TokenStream) -> TokenStream { // Construct a string representation of the type definition let s = input.to_string(); // Parse the string representation let ast = syn::parse_macro_input(&s).unwrap(); // Build the impl let gen = match ast.body { Body::Enum(_) => panic!("Enum unsporrted."), Body::Struct(ref fields) => impl_struct_iter(fields), }; // Return the generated impl gen.parse().unwrap() } /// impl_struct_iter is the meat of the struct_iterator method. It takes a fragment of rust code /// specifically a tuple of types, and outputs code implementing `Zero` (from struct_gen) on these /// types for arrays of fixed-length between [0,10]. fn impl_struct_iter(fields: &VariantData) -> quote::Tokens { // capture all the types to impl Zero on let mut idents = Vec::new(); // This is some ugly nested matching. // TODO: find a cleaner way to do this. match fields { VariantData::Tuple(ref fields) => { for (_, field) in fields.iter().enumerate() { match &field.ty { Path(_, ref f) => { let ident = &f.segments[0].ident; idents.push(ident); } _ => panic!("Unexpected tuple field."), } } } _ => panic!("Unsupported variant data."), } // the resolved code let mut res = Vec::new(); // If greater than 10, use a std::vec::Vec. for i in 0..=10 { for x in idents.iter() { let size = i as usize; res.push(quote! { impl_zero!( [#x; #size], [<#x>::default(); #size] ); }); } } // return the rust code fragment quote! { #(#res)* } }