mutant_kraken_macros/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4
5#[derive(serde::Deserialize)]
6struct JsonItem {
7    r#type: String,
8    named: bool,
9}
10
11#[proc_macro]
12pub fn generate_kotlin_types_enum(_input: TokenStream) -> TokenStream {
13    const JSON_DATA: &str = tree_sitter_kotlin::NODE_TYPES;
14    let json_items: Vec<JsonItem> = serde_json::from_str(JSON_DATA).expect("Failed to parse JSON");
15
16    let mut named_types: Vec<_> = json_items
17        .iter()
18        .filter(|item| item.named)
19        .map(|item| snake_to_camel(&item.r#type))
20        .collect();
21    // Add custom types
22    named_types.push("ERROR".into());
23    named_types.push("Remove".into());
24    named_types.push("RemoveOperator".into());
25    named_types.push("AnyParent".into());
26
27    let non_named_types: Vec<_> = json_items
28        .iter()
29        .filter(|item| !item.named)
30        .map(|item| &item.r#type)
31        .collect::<Vec<_>>();
32
33    let enum_variants = named_types.iter().map(|name| {
34        let variant_name = syn::Ident::new(name, proc_macro2::Span::call_site());
35        quote! {
36            #variant_name,
37        }
38    });
39
40    let display_match_arms = named_types.iter().map(|name| {
41        let variant_name = syn::Ident::new(name, proc_macro2::Span::call_site());
42        quote! {
43            KotlinTypes::#variant_name => write!(f, "{}", #name),
44        }
45    });
46
47    let convert_match_arms = named_types.iter().map(|name| {
48        let variant_name = syn::Ident::new(name, proc_macro2::Span::call_site());
49        quote! {
50            #name => KotlinTypes::#variant_name,
51        }
52    });
53
54    let expanded = quote! {
55        pub const NON_NAMED_TYPES: &[&str] = &[#(#non_named_types),*];
56        #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
57        pub enum KotlinTypes {
58            #(#enum_variants)*
59            NonNamedType(String)
60        }
61
62        impl std::fmt::Display for KotlinTypes {
63            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64                match self {
65                    #(#display_match_arms)*
66                    KotlinTypes::NonNamedType(s) => write!(f, "{}", s)
67                }
68            }
69        }
70
71        impl KotlinTypes {
72            pub fn new(s: &str) -> std::result::Result<KotlinTypes, String> {
73                if NON_NAMED_TYPES.contains(&s) {
74                    return Ok(KotlinTypes::NonNamedType(s.to_string()))
75                }
76                let name: String = s.split('_')
77                    .map(|p| {
78                        if !p.is_empty() {
79                            let mut v: Vec<char> = p.chars().collect();
80                            v[0] = v[0].to_uppercase().next().unwrap();
81                            let x: String = v.into_iter().collect();
82                            x
83                        } else {
84                            p.to_string()
85                        }
86                    })
87                    .collect();
88                let res = match name.as_str() {
89                    #(#convert_match_arms)*
90                    _ => {
91                        return Err(format!("could not convert {} to kotlin type", s))
92                    }
93                };
94
95                Ok(res)
96            }
97
98            pub fn as_str(&self) -> String {
99                let mut second_upper = 0;
100                let mut x = format!("{}", *self);
101                x.as_bytes().iter().enumerate().for_each(|(i, c)| {
102                    if (*c as char).is_uppercase() && i != 0 {
103                        second_upper = i
104                    }
105                });
106                x = x.to_lowercase();
107                if second_upper != 0 {
108                    x.insert(second_upper, '_');
109                }
110                x
111            }
112        }
113    };
114
115    TokenStream::from(expanded)
116}
117
118fn snake_to_camel(name: &str) -> String {
119    // convert snake_case to camelcase
120    name.split('_')
121        .map(|p| {
122            if !p.is_empty() {
123                let mut v: Vec<char> = p.chars().collect();
124                v[0] = v[0].to_uppercase().next().unwrap();
125                let x: String = v.into_iter().collect();
126                x
127            } else {
128                p.to_string()
129            }
130        })
131        .collect()
132}