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
use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::quote; use syn::{parse_macro_input, ItemFn}; #[proc_macro_attribute] pub fn giver(attr: TokenStream, item: TokenStream) -> TokenStream { let func = parse_macro_input!(item as ItemFn); let str_name_snake = func.sig.ident.to_string(); let str_name_pascal = to_pascal_case(&str_name_snake); let func_name = make_ident(&str_name_snake); let mod_name = make_ident(&(str_name_snake.clone() + "_mod")); let state_enum_name = make_ident(&(str_name_pascal.clone() + "State")); let struct_name = make_ident(&str_name_pascal); let new_code = quote! { mod #mod_name { enum #state_enum_name { Start, Done } pub struct #struct_name { state: #state_enum_name, } impl Iterator for #struct_name { type Item = i64; fn next(&mut self) -> Option<i64> { loop { match self.state { #state_enum_name::Start => { self.state = #state_enum_name::Done; return Some(1); }, #state_enum_name::Done => { return None }, } } } } pub fn #func_name() -> #struct_name { #struct_name { state: #state_enum_name::Start } } } use #mod_name::#func_name; }; if attr.to_string() == "print" { println!("{}", &new_code); } TokenStream::from(new_code) } fn to_pascal_case(snake_case_str: &str) -> String { let mut result = String::new(); for chunk in snake_case_str.split("_") { if let Some(c) = chunk.chars().nth(0) { result += &c.to_uppercase().to_string(); result += &chunk[1..]; } } return result; } fn make_ident(str: &str) -> Ident { Ident::new(str, Span::call_site()) }