tauri_typegen/analysis/
type_resolver.rs

1use std::collections::HashMap;
2
3/// Type resolver for mapping Rust types to TypeScript types
4#[derive(Debug)]
5pub struct TypeResolver {
6    type_mappings: HashMap<String, String>,
7}
8
9impl TypeResolver {
10    pub fn new() -> Self {
11        let mut type_mappings = HashMap::new();
12
13        // Basic Rust to TypeScript mappings
14        type_mappings.insert("String".to_string(), "string".to_string());
15        type_mappings.insert("&str".to_string(), "string".to_string());
16        type_mappings.insert("str".to_string(), "string".to_string());
17        type_mappings.insert("i8".to_string(), "number".to_string());
18        type_mappings.insert("i16".to_string(), "number".to_string());
19        type_mappings.insert("i32".to_string(), "number".to_string());
20        type_mappings.insert("i64".to_string(), "number".to_string());
21        type_mappings.insert("i128".to_string(), "number".to_string());
22        type_mappings.insert("isize".to_string(), "number".to_string());
23        type_mappings.insert("u8".to_string(), "number".to_string());
24        type_mappings.insert("u16".to_string(), "number".to_string());
25        type_mappings.insert("u32".to_string(), "number".to_string());
26        type_mappings.insert("u64".to_string(), "number".to_string());
27        type_mappings.insert("u128".to_string(), "number".to_string());
28        type_mappings.insert("usize".to_string(), "number".to_string());
29        type_mappings.insert("f32".to_string(), "number".to_string());
30        type_mappings.insert("f64".to_string(), "number".to_string());
31        type_mappings.insert("bool".to_string(), "boolean".to_string());
32        type_mappings.insert("()".to_string(), "void".to_string());
33
34        // Collection type mappings
35        type_mappings.insert("HashMap".to_string(), "Map".to_string());
36        type_mappings.insert("BTreeMap".to_string(), "Map".to_string());
37        type_mappings.insert("HashSet".to_string(), "Set".to_string());
38        type_mappings.insert("BTreeSet".to_string(), "Set".to_string());
39
40        Self { type_mappings }
41    }
42
43    /// Map a Rust type string to TypeScript type string
44    pub fn map_rust_type_to_typescript(&mut self, rust_type: &str) -> String {
45        // Handle Option<T> -> T | null
46        if let Some(inner_type) = self.extract_option_inner_type(rust_type) {
47            let mapped_inner = self.map_rust_type_to_typescript(&inner_type);
48            return format!("{} | null", mapped_inner);
49        }
50
51        // Handle Result<T, E> -> T (ignore error type for TypeScript)
52        if let Some(ok_type) = self.extract_result_ok_type(rust_type) {
53            return self.map_rust_type_to_typescript(&ok_type);
54        }
55
56        // Handle Vec<T> -> T[]
57        if let Some(inner_type) = self.extract_vec_inner_type(rust_type) {
58            let mapped_inner = self.map_rust_type_to_typescript(&inner_type);
59            return format!("{}[]", mapped_inner);
60        }
61
62        // Handle HashMap<K, V> -> Map<K, V>
63        if let Some((key_type, value_type)) = self.extract_hashmap_types(rust_type) {
64            let mapped_key = self.map_rust_type_to_typescript(&key_type);
65            let mapped_value = self.map_rust_type_to_typescript(&value_type);
66            return format!("Map<{}, {}>", mapped_key, mapped_value);
67        }
68
69        // Handle BTreeMap<K, V> -> Map<K, V>
70        if let Some((key_type, value_type)) = self.extract_btreemap_types(rust_type) {
71            let mapped_key = self.map_rust_type_to_typescript(&key_type);
72            let mapped_value = self.map_rust_type_to_typescript(&value_type);
73            return format!("Map<{}, {}>", mapped_key, mapped_value);
74        }
75
76        // Handle HashSet<T> -> T[] (arrays for JSON compatibility)
77        if let Some(inner_type) = self.extract_hashset_inner_type(rust_type) {
78            let mapped_inner = self.map_rust_type_to_typescript(&inner_type);
79            return format!("{}[]", mapped_inner);
80        }
81
82        // Handle BTreeSet<T> -> T[]
83        if let Some(inner_type) = self.extract_btreeset_inner_type(rust_type) {
84            let mapped_inner = self.map_rust_type_to_typescript(&inner_type);
85            return format!("{}[]", mapped_inner);
86        }
87
88        // Handle tuple types (String, i32) -> [string, number]
89        if let Some(tuple_types) = self.extract_tuple_types(rust_type) {
90            if tuple_types.is_empty() {
91                return "void".to_string();
92            }
93            let mapped_types: Vec<String> = tuple_types
94                .iter()
95                .map(|t| self.map_rust_type_to_typescript(t.trim()))
96                .collect();
97            return format!("[{}]", mapped_types.join(", "));
98        }
99
100        // Handle reference types &T -> T
101        if let Some(inner_type) = self.extract_reference_type(rust_type) {
102            return self.map_rust_type_to_typescript(&inner_type);
103        }
104
105        // Direct mapping lookup
106        if let Some(mapped) = self.type_mappings.get(rust_type) {
107            return mapped.clone();
108        }
109
110        // If no mapping found, assume it's a custom type and return as-is
111        rust_type.to_string()
112    }
113
114    /// Extract inner type from Option<T>
115    fn extract_option_inner_type(&self, rust_type: &str) -> Option<String> {
116        if rust_type.starts_with("Option<") && rust_type.ends_with('>') {
117            let inner = &rust_type[7..rust_type.len() - 1];
118            Some(inner.to_string())
119        } else {
120            None
121        }
122    }
123
124    /// Extract OK type from Result<T, E>
125    fn extract_result_ok_type(&self, rust_type: &str) -> Option<String> {
126        if rust_type.starts_with("Result<") && rust_type.ends_with('>') {
127            let inner = &rust_type[7..rust_type.len() - 1];
128            if let Some(comma_pos) = inner.find(',') {
129                let ok_type = inner[..comma_pos].trim();
130                Some(ok_type.to_string())
131            } else {
132                Some(inner.to_string())
133            }
134        } else {
135            None
136        }
137    }
138
139    /// Extract inner type from Vec<T>
140    fn extract_vec_inner_type(&self, rust_type: &str) -> Option<String> {
141        if rust_type.starts_with("Vec<") && rust_type.ends_with('>') {
142            let inner = &rust_type[4..rust_type.len() - 1];
143            Some(inner.to_string())
144        } else {
145            None
146        }
147    }
148
149    /// Extract key and value types from HashMap<K, V>
150    fn extract_hashmap_types(&self, rust_type: &str) -> Option<(String, String)> {
151        if rust_type.starts_with("HashMap<") && rust_type.ends_with('>') {
152            let inner = &rust_type[8..rust_type.len() - 1];
153            self.parse_two_type_params(inner)
154        } else {
155            None
156        }
157    }
158
159    /// Extract key and value types from BTreeMap<K, V>
160    fn extract_btreemap_types(&self, rust_type: &str) -> Option<(String, String)> {
161        if rust_type.starts_with("BTreeMap<") && rust_type.ends_with('>') {
162            let inner = &rust_type[9..rust_type.len() - 1];
163            self.parse_two_type_params(inner)
164        } else {
165            None
166        }
167    }
168
169    /// Extract inner type from HashSet<T>
170    fn extract_hashset_inner_type(&self, rust_type: &str) -> Option<String> {
171        if rust_type.starts_with("HashSet<") && rust_type.ends_with('>') {
172            let inner = &rust_type[8..rust_type.len() - 1];
173            Some(inner.to_string())
174        } else {
175            None
176        }
177    }
178
179    /// Extract inner type from BTreeSet<T>
180    fn extract_btreeset_inner_type(&self, rust_type: &str) -> Option<String> {
181        if rust_type.starts_with("BTreeSet<") && rust_type.ends_with('>') {
182            let inner = &rust_type[9..rust_type.len() - 1];
183            Some(inner.to_string())
184        } else {
185            None
186        }
187    }
188
189    /// Extract types from tuple (T1, T2, ...)
190    fn extract_tuple_types(&self, rust_type: &str) -> Option<Vec<String>> {
191        if rust_type.starts_with('(') && rust_type.ends_with(')') {
192            let inner = &rust_type[1..rust_type.len() - 1];
193            if inner.trim().is_empty() {
194                return Some(vec![]);
195            }
196            let types: Vec<String> = inner.split(',').map(|s| s.trim().to_string()).collect();
197            Some(types)
198        } else {
199            None
200        }
201    }
202
203    /// Extract inner type from reference &T
204    fn extract_reference_type(&self, rust_type: &str) -> Option<String> {
205        rust_type
206            .strip_prefix('&')
207            .map(|stripped| stripped.to_string())
208    }
209
210    /// Parse two type parameters separated by comma (for HashMap, BTreeMap)
211    fn parse_two_type_params(&self, inner: &str) -> Option<(String, String)> {
212        let mut depth = 0;
213        let mut comma_pos = None;
214
215        for (i, ch) in inner.char_indices() {
216            match ch {
217                '<' => depth += 1,
218                '>' => depth -= 1,
219                ',' if depth == 0 => {
220                    comma_pos = Some(i);
221                    break;
222                }
223                _ => {}
224            }
225        }
226
227        if let Some(pos) = comma_pos {
228            let key_type = inner[..pos].trim().to_string();
229            let value_type = inner[pos + 1..].trim().to_string();
230            Some((key_type, value_type))
231        } else {
232            None
233        }
234    }
235
236    /// Get the type mappings
237    pub fn get_type_mappings(&self) -> &HashMap<String, String> {
238        &self.type_mappings
239    }
240
241    /// Add a custom type mapping
242    pub fn add_type_mapping(&mut self, rust_type: String, typescript_type: String) {
243        self.type_mappings.insert(rust_type, typescript_type);
244    }
245}
246
247impl Default for TypeResolver {
248    fn default() -> Self {
249        Self::new()
250    }
251}