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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
extern crate proc_macro;
use std::collections::HashMap;
use proc_macro::{TokenStream,TokenTree};


#[proc_macro_attribute]
pub fn reflective(attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut code = item.to_string();

    let tokens = item.clone().into_iter().peekable();
    let struct_body : TokenStream;
    let mut struct_props = HashMap::new();

    for t in tokens{
        match t{
            TokenTree::Group(struct_body) => {
                // Struct fields

                let mut struct_iter = struct_body.stream().into_iter();
                let mut field_name = String::new();
                let mut field_type = String::new();
                loop {    
                    let struct_body_token =  match struct_iter.next() {
                        None => break,
                        Some(t) => t,
                    }; 
                    match &struct_body_token {
                        TokenTree::Ident(ident) => {
                            field_name = ident.to_string(); //First identifier
                        },
                        TokenTree::Punct(punct) => {
                            if punct.as_char() == ':'{
                                let mut brak_ref = 0;
                                loop {
                                    let line_token = match struct_iter.next(){
                                            None => break,
                                            Some(t) => t,
                                        };
                                    match &line_token{
                                        TokenTree::Punct(line_p) =>{
                                            match line_p.as_char() {
                                                ',' | '}' => {
                                                    if brak_ref == 0{
                                                        struct_props.insert(field_name, field_type);
                                                        field_name = String::new();
                                                        field_type = String::new();
                                                        break;
                                                    } //end of field def
                                                },
                                                '<' => { brak_ref +=1; },
                                                '>' => { brak_ref-=1; },
                                                _ => {},
                                            }
                                        },
                                        TokenTree::Ident(_)=> {
                                            if !field_type.ends_with('\''){
                                                field_type.push(' ');
                                            }
                                        },
                                        _ => {},
                                    }
                                    field_type.push_str(&line_token.to_string());
                                }
                            }
                            continue;
                        },
                        _ => {},
                    }
                }
            },
            _ => {},
        }
    }

    let struct_name = item
        .into_iter()
        .filter(|x| match x{ 
            TokenTree::Ident(_) => true,
            _ => false,
        })
        .last()
        .unwrap()
        .to_string();
    


    let mut get_body = String::new();
    get_body.push_str("{ match field \n\r{");
    for (field,datatype) in &struct_props {
        get_body.push_str(&format!(
            "\"{field}\" => &(s.{field}) as &dyn std::any::Any,\n\r",
            field=field
        ));
    }
    get_body.push_str("_ => panic!(\"Invalid field.\"), }}");

    let mut set_body = String::new();
    set_body.push_str("match field {");
    for (field,datatype) in &struct_props {
        set_body.push_str(&format!("
            \"{field}\" => {{ s.{field} = ((&val as &dyn std::any::Any).downcast_ref::<{datatype}>().unwrap()).to_owned(); }},\r\n",
            field=field,
            datatype=datatype
        ));

    }
    set_body.push_str("_ => { panic!(\"invalid field\"); } }");

    code.push_str(&format!("
impl {struct_name} {{
    {access_nodifier} fn get_field<T: Copy + 'static>(s: &{struct_name}, field: &str) -> T {{
        let a : &dyn std::any::Any = {get_body};
        *(a.downcast_ref::<T>().unwrap())
    }}

    {access_nodifier} fn set_field<T: Copy + 'static>(s: &mut {struct_name}, field: &str, val : T) {{
        {set_body}
    }}
}}
", struct_name=struct_name, access_nodifier=attr.to_string(), get_body=get_body, set_body=set_body));

    println!("{}", code);
    code.parse().expect("Generated invalid tokens")
}