extern crate proc_macro; use proc_macro::{TokenStream,TokenTree,Ident,Delimiter}; #[proc_macro_derive(CrimtagMappable)] pub fn derive_crimtag_mappable(s: TokenStream) -> TokenStream { let mut i = s.into_iter(); while let Some(t) = i.next() { if let TokenTree::Ident(ident) = t && ident.to_string() == "struct" { break; } } let name = if let Some(TokenTree::Ident(ident)) = i.next() { ident.to_string() } else { panic!("No name found in struct."); }; // TODO: Expand this to include the generic parameters and the where? at // least the parameters. println!("struct name: {:?}", name); let mut newfunc = format!( r#"impl<'a> crimtag::MappedStructure<'a> for {} {{ fn get_value(&'a self, id: &str) -> Option> {{ match id {{ "#, name); // We're going to cheat. Every field name is followed directly by a colon, // so all we really need to do is hunt for colons and then take the // previous item. while let Some(n) = i.next() { if let TokenTree::Group(g) = &n && g.delimiter() == Delimiter::Brace { // Operate on the struct members. let mut gi = g.stream().into_iter(); let mut cur = 0usize; let mut tok = [gi.next(), gi.next()]; loop { if let Some(TokenTree::Punct(ch)) = &tok[(cur+1)%2] && ch.as_char() == ':' && let Some(TokenTree::Ident(id)) = &tok[cur] { println!("!!!!!!!!!! -> {}", id); newfunc.push_str(&format!( r#" "{}" => Some(self.{}.into()), "#, id, id)); } println!(" - {:?} {:?}", tok[cur], tok[(cur+1)%2] ); // Update next. if let Some(ni) = gi.next() { tok[cur].replace( ni ); cur = (cur+1)%2; } else { break; } } } } newfunc.push_str( r#" _ => None, } } }"#); println!(); println!(); println!("{}", newfunc); println!(); println!(); newfunc.parse().unwrap() }