jvm_macro/
lib.rs

1extern crate proc_macro;
2extern crate proc_macro2;
3#[macro_use]
4extern crate quote;
5extern crate syn;
6extern crate serde_derive_internals;
7extern crate serde;
8
9use proc_macro2::TokenStream;
10use std::str::FromStr;
11use proc_macro2::TokenTree;
12
13#[proc_macro_attribute]
14pub fn jvm_object(metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15
16    let input: TokenStream = input.clone().into();
17
18    let tokens = input.clone().into_iter().peekable();
19    let mut struct_props = Vec::new();
20    let mut inner_types = vec![];
21
22    for t in tokens {
23
24        match t{
25            TokenTree::Group(struct_body) => {
26                let mut struct_iter = struct_body.stream().into_iter();
27                let mut field_name = String::new();
28                let mut field_type = String::new();
29                loop {
30                    let struct_body_token =  match struct_iter.next() {
31                        None => break,
32                        Some(t) => t,
33                    };
34                    match &struct_body_token {
35                        TokenTree::Ident(ident) => {
36                            field_name = ident.to_string();
37                        },
38                        TokenTree::Punct(punct) => {
39                            if punct.as_char() == ':'{
40                                let mut brak_ref = 0;
41                                loop {
42                                    let line_token = match struct_iter.next() {
43                                        None => {
44                                            if field_type.replace(" ", "").len() > 3 && field_type.trim_start().ne("String") {
45                                                inner_types.push((field_name.clone().replace(" ", ""), field_type.clone().replace(" ", "")));
46                                            }
47                                            struct_props.push ((field_name.clone(), field_type.clone().replace(" ", "")));
48                                            break
49                                        },
50                                        Some(t) => t,
51                                    };
52
53                                    match &line_token{
54                                        TokenTree::Punct(line_p) => {
55                                            match line_p.as_char() {
56                                                ',' | '}' => {
57                                                    if brak_ref == 0 {
58                                                        if field_type.replace(" ", "").len() > 3 && field_type.trim_start().ne("String") {
59                                                            inner_types.push((field_name.clone().replace(" ", ""), field_type.replace(" ", "")));
60                                                        }
61                                                        struct_props.push((field_name, field_type.replace(" ", "")));
62                                                        field_name = String::new();
63                                                        field_type = String::new();
64                                                        break;
65                                                    }
66                                                },
67                                                '<' => { brak_ref +=1; },
68                                                '>' => { brak_ref-=1; },
69                                                _ => {},
70                                            }
71                                        },
72                                        TokenTree::Ident(_) => {
73                                            if !field_type.ends_with('\'') {
74                                                field_type.push(' ');
75                                            }
76                                        },
77                                        _ => {},
78                                    }
79                                    field_type.push_str(&line_token.to_string());
80                                }
81                            }
82                            continue;
83                        },
84                        _ => {},
85                    }
86                }
87            },
88            _ => {},
89        }
90    }
91
92    let struct_name = input.clone().into_iter().filter(|x| match x {
93            TokenTree::Ident(_) => true,
94            _ => false,
95        }).last().unwrap().to_string();
96
97    let output = quote! {
98        #[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Debug, Default)]
99        #input
100    }; 
101    let mut out_as_string = output.to_string();
102    let jvm_params = metadata.to_string().replace(" ", "");
103    let jvm_params: Vec<&str> = jvm_params.split(',').collect();
104
105    let mut get_body = String::new();
106    get_body.push_str("{ match field \n\r{");
107    for (field, _datatype) in &struct_props {
108        get_body.push_str(&format!(
109            "\"{field}\" => &(s.{field}) as &dyn std::any::Any,\n\r",
110            field=field
111        ));
112    }
113    get_body.push_str("_ => panic!(\"Invalid field.\"), }}");
114
115    let mut set_body = String::new();
116    set_body.push_str("match field {");
117    for (field,datatype) in &struct_props {
118        set_body.push_str(&format!("
119            \"{field}\" => {{ s.{field} = ((&val as &dyn std::any::Any).downcast_ref::<{datatype}>().unwrap()).to_owned(); }},\r\n",
120                                   field=field,
121                                   datatype=datatype
122        ));
123
124    }
125    set_body.push_str("_ => { panic!(\"invalid field\"); } }");
126
127    let mut struct_props_as_string = String::from("[");
128    let mut idx = 0;
129    for (key, value) in &struct_props {
130        struct_props_as_string.push_str(&format!("(\"{}\".to_string(), \"{}\".to_string(), {}),", key, value, idx));
131        idx += 1;
132    }
133    struct_props_as_string.remove(struct_props_as_string.len()-1);
134    struct_props_as_string.push_str("].iter().cloned().collect()");
135
136    let mut items = String::new();
137    let mut string_of_get_items = String::new();
138    let mut count = 1;
139    for inner in inner_types {
140        string_of_get_items.push_str(&format!(" #[inline]\nfn get_item{}(&self) -> Option<&Self::Item{}> ", count, count));
141        string_of_get_items.push_str("{");
142        string_of_get_items.push_str(&format!(" Some(&self.{}) ", inner.0));
143        string_of_get_items.push_str("}\n");
144
145        items.push_str(&format!(" type Item{} = {};\n ", count, inner.1));
146
147        count += 1;
148    }
149    for i in count..6 {
150        string_of_get_items.push_str(&format!(" #[inline]\nfn get_item{}(&self) -> Option<&Self::Item{}> ", i, i));
151        string_of_get_items.push_str("{ None }\n");
152        items.push_str(&format!(" type Item{} = {}; \n ", i, &struct_name));
153    }
154
155
156    let impl_value = r#"
157    impl Serializable for {{struct_name}} {
158
159        {{items}}
160
161        #[inline]
162        fn java_class_name (&self) -> String {
163            "{{jvm_class}}".to_string()
164        }
165
166        #[inline]
167        fn serial_version_uid(&self) -> u64 {
168            {{jvm_uid}}
169        }
170
171        #[inline]
172        fn get_field<T: std::any::Any + Clone + 'static>(s: &Self, field: &str) -> T {{
173            let a : &dyn std::any::Any = {{get_body}};
174            (a.downcast_ref::<T>().unwrap().clone())
175        }}
176
177        #[inline]
178        fn set_field<T: std::any::Any + Clone + 'static>(s: &mut Self, field: &str, val : T) {{
179            {{set_body}}
180        }}
181
182        #[inline]
183        fn get_fields(&self) -> Vec<(String, String, i32)> {
184            {{fields_string}}
185        }
186
187        {{get_items}}
188
189    }"#;
190    out_as_string.push_str(&impl_value.replace("{{struct_name}}", &struct_name)
191        .replace("{{jvm_class}}", &jvm_params[0])
192        .replace("{{jvm_uid}}", &jvm_params[1])
193        .replace("{{set_body}}", &set_body)
194        .replace("{{get_body}}", &get_body)
195        .replace("{{fields_string}}", &struct_props_as_string)
196        .replace("{{get_items}}", &string_of_get_items)
197        .replace("{{items}}", &items)
198    );
199    proc_macro::TokenStream::from_str(&out_as_string).unwrap()
200}