reflective/
lib.rs

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                // Struct fields
18
19                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(); //First identifier
30                        },
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                                                    } //end of field def
49                                                },
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}