typescriptify_derive/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3#[macro_use]
4extern crate quote;
5
6use proc_macro::TokenStream;
7
8#[proc_macro_derive(TypeScriptify)]
9pub fn hello_world(input: TokenStream) -> TokenStream {
10    // Construct a string representation of the type definition
11    let s = input.to_string();
12
13    // Parse the string representation
14    let ast = syn::parse_derive_input(&s).unwrap();
15
16    // Build the impl
17    let gen = impl_hello_world(&ast);
18
19    // Return the generated impl
20    gen.parse().unwrap()
21}
22
23
24fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {
25    let name = &ast.ident;
26    let structname = name.to_string();
27    let _body = &ast.body;
28    let mut complete_string: String = "".to_string();
29    let _n = match ast.body {
30        syn::Body::Struct(ref data) => {
31            let mut fieldlines: Vec<String> = vec![];
32            for field in data.fields() {
33                //field.ty; //type of the field
34                //field.ident; //name (tuple struct fields have none here)
35                //field.vis; //visibility
36                //field.attrs; //attributes
37                //println!("Fieldtype = {:?} and Name = {:?}", field.ty, field.ident);
38                let fieldname: String = format!("{}", field.ident.clone().unwrap().to_string());
39                match field.ty {
40                    syn::Ty::Array(ref _b, ref _c) => {
41                        unimplemented!()
42                    }
43                    syn::Ty::Ptr(ref _p) => {
44                        unimplemented!()
45                    }
46                    syn::Ty::Path(ref _qselfopt, ref path) => {
47                        let intype = format!("{}", path.segments.last().unwrap().ident);
48                        let generic_params_unformated = &path.segments.last().clone().unwrap().parameters;
49                        let mut generics_parameters: Vec<String> = Vec::new();
50                        match generic_params_unformated {
51                            &syn::PathParameters::AngleBracketed(ref angle_bracketed_parameter_data) => {
52                                for ty in &angle_bracketed_parameter_data.types {
53                                    match ty {
54                                        &syn::Ty::Path(ref _qotherself, ref qotherpath) => {
55                                            generics_parameters.push(format!("{}", qotherpath.segments.last().unwrap().ident));
56                                        }
57                                        _ => unimplemented!(),
58                                    }
59                                }
60                            }
61                            _ => unimplemented!(),
62                        };
63                        //treat option special, as types in typescript are already nullable
64                        let mtyp: String = if intype.eq("Option") {
65                            (match generics_parameters.first().unwrap().as_ref() {
66                                "i8" => "number",
67                                "i16" => "number",
68                                "i32" => "number",
69                                "i64" => "number",
70                                "u8" => "number",
71                                "u16" => "number",
72                                "u32" => "number",
73                                "u64" => "number",
74                                "usize" => "number",
75                                "bool" => "boolean",
76                                "String" => "string",
77                                "f32" => "number",
78                                "f64" => "number",
79                                "HashMap" => "Map",
80                                "Vec" => "Array",
81                                "HashSet" => "Array",
82                                "Value" => "any",
83                                a @ _ => a,
84                            }).to_string()
85                        } else {
86                            let mut generic_term_in_angle_brackets: String = if generics_parameters.is_empty() { "".to_string() } else { "<".to_string() };
87                            for gen in &generics_parameters {
88                                if generic_term_in_angle_brackets.len() > 1 {
89                                    generic_term_in_angle_brackets = generic_term_in_angle_brackets + ", ";
90                                }
91                                generic_term_in_angle_brackets = generic_term_in_angle_brackets + match gen.as_ref() {
92                                    "i8" => "number",
93                                    "i16" => "number",
94                                    "i32" => "number",
95                                    "i64" => "number",
96                                    "u8" => "number",
97                                    "u16" => "number",
98                                    "u32" => "number",
99                                    "u64" => "number",
100                                    "usize" => "number",
101                                    "bool" => "boolean",
102                                    "String" => "string",
103                                    "f32" => "number",
104                                    "f64" => "number",
105                                    "HashMap" => "Map",
106                                    "Vec" => "Array",
107                                    "HashSet" => "Array",
108                                    "Value" => "any",
109                                    a @ _ => a,
110                                };
111                            }
112                            if !generics_parameters.is_empty() {
113                                generic_term_in_angle_brackets = generic_term_in_angle_brackets + ">";
114                            }
115                            (match intype.as_ref() {
116                                "i8" => "number".to_string(),
117                                "i16" => "number".to_string(),
118                                "i32" => "number".to_string(),
119                                "i64" => "number".to_string(),
120                                "u8" => "number".to_string(),
121                                "u16" => "number".to_string(),
122                                "u32" => "number".to_string(),
123                                "u64" => "number".to_string(),
124                                "usize" => "number".to_string(),
125                                "bool" => "boolean".to_string(),
126                                "String" => "string".to_string(),
127                                "f32" => "number".to_string(),
128                                "f64" => "number".to_string(),
129                                "HashMap" => "Map".to_string(),
130                                "Vec" => "Array".to_string(),
131                                "HashSet" => "Array".to_string(),
132                                "Value" => "any".to_string(),
133                                a @ _ => a.to_string(),
134                            } + &generic_term_in_angle_brackets)
135                        };
136                        fieldlines.push(format!("{}: {};", fieldname, mtyp));
137                    }
138                    _ => unimplemented!(),
139                }
140            }
141
142
143            let mut s = "".to_string();
144            for fieldline in fieldlines {
145                s = s + "    " + &fieldline + "\n";
146            }
147            complete_string = format!("export interface {} {{\n{}}}", structname, s);
148            data.fields().len()
149        }
150        syn::Body::Enum(ref variant_vec) => {
151            let k = variant_vec.len();
152            let enum_name = format!("{}", name);
153            let mut variants: Vec<String> = Vec::new();
154            for variant in variant_vec {
155                let mut fieldlines: Vec<String> = vec![];
156                let variant_name = format!("{}", variant.ident);
157                variants.push(variant_name.to_string());
158                let data = &variant.data;
159
160                //add each variant as a field with the variant name as field name, and also its type
161
162                for field in data.fields() {
163                    //field.ty; //type of the field
164                    //field.ident; //name (tuple struct fields have none here)
165                    //field.vis; //visibility
166                    //field.attrs; //attributes
167                    //println!("Fieldtype = {:?} and Name = {:?}", field.ty, field.ident);
168                    let fieldname: String = format!("{}", field.ident.clone().unwrap().to_string());
169                    match field.ty {
170                        syn::Ty::Array(ref _b, ref _c) => {
171                            unimplemented!()
172                        }
173                        syn::Ty::Ptr(ref _p) => {
174                            unimplemented!()
175                        }
176                        syn::Ty::Path(ref _qselfopt, ref path) => {
177                            let intype = format!("{}", path.segments.last().unwrap().ident);
178                            let generic_params_unformated = &path.segments.last().clone().unwrap().parameters;
179                            let mut generics_parameters: Vec<String> = Vec::new();
180                            match generic_params_unformated {
181                                &syn::PathParameters::AngleBracketed(ref angle_bracketed_parameter_data) => {
182                                    for ty in &angle_bracketed_parameter_data.types {
183                                        match ty {
184                                            &syn::Ty::Path(ref _qotherself, ref qotherpath) => {
185                                                generics_parameters.push(format!("{}", qotherpath.segments.last().unwrap().ident));
186                                            }
187                                            _ => unimplemented!(),
188                                        }
189                                    }
190                                }
191                                _ => unimplemented!(),
192                            };
193                            //treat option special, as types in typescript are already nullable
194                            let mtyp: String = if intype.eq("Option") {
195                                (match generics_parameters.first().unwrap().as_ref() {
196                                    "i8" => "number",
197                                    "i16" => "number",
198                                    "i32" => "number",
199                                    "i64" => "number",
200                                    "u8" => "number",
201                                    "u16" => "number",
202                                    "u32" => "number",
203                                    "u64" => "number",
204                                    "usize" => "number",
205                                    "bool" => "boolean",
206                                    "String" => "string",
207                                    "f32" => "number",
208                                    "f64" => "number",
209                                    "HashMap" => "Map",
210                                    "Vec" => "Array",
211                                    "HashSet" => "Array",
212                                    "Value" => "any",
213                                    a @ _ => a,
214                                }).to_string()
215                            } else {
216                                let mut generic_term_in_angle_brackets: String = if generics_parameters.is_empty() { "".to_string() } else { "<".to_string() };
217                                for gen in &generics_parameters {
218                                    if generic_term_in_angle_brackets.len() > 1 {
219                                        generic_term_in_angle_brackets = generic_term_in_angle_brackets + ", ";
220                                    }
221                                    generic_term_in_angle_brackets = generic_term_in_angle_brackets + match gen.as_ref() {
222                                        "i8" => "number",
223                                        "i16" => "number",
224                                        "i32" => "number",
225                                        "i64" => "number",
226                                        "u8" => "number",
227                                        "u16" => "number",
228                                        "u32" => "number",
229                                        "u64" => "number",
230                                        "usize" => "number",
231                                        "bool" => "boolean",
232                                        "String" => "string",
233                                        "f32" => "number",
234                                        "f64" => "number",
235                                        "HashMap" => "Map",
236                                        "Vec" => "Array",
237                                        "HashSet" => "Array",
238                                        "Value" => "any",
239                                        a @ _ => a,
240                                    };
241                                }
242                                if !generics_parameters.is_empty() {
243                                    generic_term_in_angle_brackets = generic_term_in_angle_brackets + ">";
244                                }
245                                (match intype.as_ref() {
246                                    "i8" => "number".to_string(),
247                                    "i16" => "number".to_string(),
248                                    "i32" => "number".to_string(),
249                                    "i64" => "number".to_string(),
250                                    "u8" => "number".to_string(),
251                                    "u16" => "number".to_string(),
252                                    "u32" => "number".to_string(),
253                                    "u64" => "number".to_string(),
254                                    "usize" => "number".to_string(),
255                                    "bool" => "boolean".to_string(),
256                                    "String" => "string".to_string(),
257                                    "f32" => "number".to_string(),
258                                    "f64" => "number".to_string(),
259                                    "HashMap" => "Map".to_string(),
260                                    "Vec" => "Array".to_string(),
261                                    "HashSet" => "Array".to_string(),
262                                    "Value" => "any".to_string(),
263                                    a @ _ => a.to_string(),
264                                } + &generic_term_in_angle_brackets)
265                            };
266                            fieldlines.push(format!("{}: {};", fieldname, mtyp));
267                        }
268                        _ => unimplemented!(),
269                    }
270                }
271
272
273                let mut s = "".to_string();
274                for fieldline in &fieldlines {
275                    s = s + "    " + fieldline.as_ref() + "\n";
276                }
277
278                complete_string = complete_string + &format!("export interface {} {{\n{}}}\n\n", variant_name, s);
279            }
280
281            //Add final enum interface:
282            let mut s = "".to_string();
283            for v in variants {
284                s = s + "    " + v.as_ref() + ": " + v.as_ref() + ";\n";
285            }
286            complete_string = complete_string + &format!("export interface {} {{\n{}}}\n\n", enum_name, s);
287
288            k
289        }
290    };
291    quote! {
292        impl TypeScriptifyTrait for #name {
293            fn type_script_ify() -> String {
294                format!("{}\n", #complete_string)
295            }
296        }
297    }
298}