1extern crate proc_macro;
2use std::collections::HashMap;
3use proc_macro::{TokenStream,TokenTree};
4
5
6#[proc_macro_attribute]
7pub fn reflective(attr: TokenStream, item: TokenStream) -> TokenStream {
8 let mut code = item.to_string();
9
10 let tokens = item.clone().into_iter().peekable();
11 let struct_body : TokenStream;
12 let mut struct_props = HashMap::new();
13
14 for t in tokens{
15 match t{
16 TokenTree::Group(struct_body) => {
17 let mut struct_iter = struct_body.stream().into_iter();
20 let mut field_name = String::new();
21 let mut field_type = String::new();
22 loop {
23 let struct_body_token = match struct_iter.next() {
24 None => break,
25 Some(t) => t,
26 };
27 match &struct_body_token {
28 TokenTree::Ident(ident) => {
29 field_name = ident.to_string(); },
31 TokenTree::Punct(punct) => {
32 if punct.as_char() == ':'{
33 let mut brak_ref = 0;
34 loop {
35 let line_token = match struct_iter.next(){
36 None => break,
37 Some(t) => t,
38 };
39 match &line_token{
40 TokenTree::Punct(line_p) =>{
41 match line_p.as_char() {
42 ',' | '}' => {
43 if brak_ref == 0{
44 struct_props.insert(field_name, field_type);
45 field_name = String::new();
46 field_type = String::new();
47 break;
48 } },
50 '<' => { brak_ref +=1; },
51 '>' => { brak_ref-=1; },
52 _ => {},
53 }
54 },
55 TokenTree::Ident(_)=> {
56 if !field_type.ends_with('\''){
57 field_type.push(' ');
58 }
59 },
60 _ => {},
61 }
62 field_type.push_str(&line_token.to_string());
63 }
64 }
65 continue;
66 },
67 _ => {},
68 }
69 }
70 },
71 _ => {},
72 }
73 }
74
75 let struct_name = item
76 .into_iter()
77 .filter(|x| match x{
78 TokenTree::Ident(_) => true,
79 _ => false,
80 })
81 .last()
82 .unwrap()
83 .to_string();
84
85
86
87 let mut get_body = String::new();
88 get_body.push_str("{ match field \n\r{");
89 for (field,datatype) in &struct_props {
90 get_body.push_str(&format!(
91 "\"{field}\" => &(s.{field}) as &dyn std::any::Any,\n\r",
92 field=field
93 ));
94 }
95 get_body.push_str("_ => panic!(\"Invalid field.\"), }}");
96
97 let mut set_body = String::new();
98 set_body.push_str("match field {");
99 for (field,datatype) in &struct_props {
100 set_body.push_str(&format!("
101 \"{field}\" => {{ s.{field} = ((&val as &dyn std::any::Any).downcast_ref::<{datatype}>().unwrap()).to_owned(); }},\r\n",
102 field=field,
103 datatype=datatype
104 ));
105
106 }
107 set_body.push_str("_ => { panic!(\"invalid field\"); } }");
108
109 code.push_str(&format!("
110impl {struct_name} {{
111 {access_nodifier} fn get_field<T: Copy + 'static>(s: &{struct_name}, field: &str) -> T {{
112 let a : &dyn std::any::Any = {get_body};
113 *(a.downcast_ref::<T>().unwrap())
114 }}
115
116 {access_nodifier} fn set_field<T: Copy + 'static>(s: &mut {struct_name}, field: &str, val : T) {{
117 {set_body}
118 }}
119}}
120", struct_name=struct_name, access_nodifier=attr.to_string(), get_body=get_body, set_body=set_body));
121
122 println!("{}", code);
123 code.parse().expect("Generated invalid tokens")
124}