1use std::collections::HashMap;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ModuleMapping {
10 pub python_module: String,
12 pub rust_crate: Option<String>,
14 pub rust_use: String,
16 pub dependencies: Vec<String>,
18 pub version: String,
20 pub wasm_compatible: WasmCompatibility,
22 pub notes: Option<String>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub enum WasmCompatibility {
28 Full,
30 Partial,
32 RequiresWasi,
34 RequiresJsInterop,
36 Incompatible,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct FunctionMapping {
42 pub python_name: String,
44 pub rust_equiv: String,
46 pub requires_use: Option<String>,
48 pub wasm_compatible: WasmCompatibility,
50 pub transform_notes: Option<String>,
52}
53
54pub struct StdlibMapper {
55 modules: HashMap<String, ModuleMapping>,
56 functions: HashMap<String, HashMap<String, FunctionMapping>>,
57}
58
59impl StdlibMapper {
60 pub fn new() -> Self {
61 let mut mapper = Self {
62 modules: HashMap::new(),
63 functions: HashMap::new(),
64 };
65 mapper.init_mappings();
66 mapper
67 }
68
69 fn init_mappings(&mut self) {
70 let comprehensive_mappings = crate::stdlib_mappings_comprehensive::init_critical_mappings();
72
73 for (module_mapping, function_mappings) in comprehensive_mappings {
74 self.add_module(module_mapping.clone());
75 for func_mapping in function_mappings {
76 self.add_function_mapping(&module_mapping.python_module, func_mapping);
77 }
78 }
79
80 self.add_legacy_mappings();
82 }
83
84 fn add_legacy_mappings(&mut self) {
85 if !self.modules.contains_key("random") {
87 self.add_module(ModuleMapping {
88 python_module: "random".to_string(),
89 rust_crate: Some("rand".to_string()),
90 rust_use: "rand".to_string(),
91 dependencies: vec![],
92 version: "0.8".to_string(),
93 wasm_compatible: WasmCompatibility::RequiresJsInterop,
94 notes: Some("Requires getrandom with js feature for WASM".to_string()),
95 });
96
97 self.add_function_mapping("random", FunctionMapping {
98 python_name: "random".to_string(),
99 rust_equiv: "rand::random::<f64>".to_string(),
100 requires_use: Some("rand".to_string()),
101 wasm_compatible: WasmCompatibility::RequiresJsInterop,
102 transform_notes: None,
103 });
104
105 self.add_function_mapping("random", FunctionMapping {
106 python_name: "randint".to_string(),
107 rust_equiv: "rand::thread_rng().gen_range".to_string(),
108 requires_use: Some("rand::Rng".to_string()),
109 wasm_compatible: WasmCompatibility::RequiresJsInterop,
110 transform_notes: Some("Takes range as (start..=end)".to_string()),
111 });
112 }
113
114 if !self.modules.contains_key("datetime") {
116 self.add_module(ModuleMapping {
117 python_module: "datetime".to_string(),
118 rust_crate: Some("chrono".to_string()),
119 rust_use: "chrono".to_string(),
120 dependencies: vec![],
121 version: "0.4".to_string(),
122 wasm_compatible: WasmCompatibility::RequiresJsInterop,
123 notes: Some("Uses JS Date in browser, native in Node.js".to_string()),
124 });
125
126 self.add_function_mapping("datetime", FunctionMapping {
127 python_name: "datetime.now".to_string(),
128 rust_equiv: "chrono::Utc::now".to_string(),
129 requires_use: Some("chrono::Utc".to_string()),
130 wasm_compatible: WasmCompatibility::RequiresJsInterop,
131 transform_notes: None,
132 });
133 }
134
135 if !self.modules.contains_key("re") {
137 self.add_module(ModuleMapping {
138 python_module: "re".to_string(),
139 rust_crate: Some("regex".to_string()),
140 rust_use: "regex::Regex".to_string(),
141 dependencies: vec![],
142 version: "1".to_string(),
143 wasm_compatible: WasmCompatibility::Full,
144 notes: None,
145 });
146
147 self.add_function_mapping("re", FunctionMapping {
148 python_name: "compile".to_string(),
149 rust_equiv: "Regex::new".to_string(),
150 requires_use: Some("regex::Regex".to_string()),
151 wasm_compatible: WasmCompatibility::Full,
152 transform_notes: None,
153 });
154
155 self.add_function_mapping("re", FunctionMapping {
156 python_name: "match".to_string(),
157 rust_equiv: "Regex::is_match".to_string(),
158 requires_use: Some("regex::Regex".to_string()),
159 wasm_compatible: WasmCompatibility::Full,
160 transform_notes: None,
161 });
162 }
163
164 if !self.modules.contains_key("sys") {
166 self.add_module(ModuleMapping {
167 python_module: "sys".to_string(),
168 rust_crate: None,
169 rust_use: "std::env".to_string(),
170 dependencies: vec![],
171 version: "*".to_string(),
172 wasm_compatible: WasmCompatibility::Partial,
173 notes: Some("Limited functionality in WASM".to_string()),
174 });
175
176 self.add_function_mapping("sys", FunctionMapping {
177 python_name: "argv".to_string(),
178 rust_equiv: "std::env::args".to_string(),
179 requires_use: Some("std::env".to_string()),
180 wasm_compatible: WasmCompatibility::Incompatible,
181 transform_notes: Some("Not available in browser WASM".to_string()),
182 });
183 }
184
185 if !self.modules.contains_key("os") {
187 self.add_module(ModuleMapping {
188 python_module: "os".to_string(),
189 rust_crate: None,
190 rust_use: "std::env".to_string(),
191 dependencies: vec![],
192 version: "*".to_string(),
193 wasm_compatible: WasmCompatibility::RequiresWasi,
194 notes: Some("Most functions require WASI".to_string()),
195 });
196
197 self.add_function_mapping("os", FunctionMapping {
198 python_name: "getcwd".to_string(),
199 rust_equiv: "std::env::current_dir".to_string(),
200 requires_use: Some("std::env".to_string()),
201 wasm_compatible: WasmCompatibility::RequiresWasi,
202 transform_notes: None,
203 });
204
205 self.add_function_mapping("os", FunctionMapping {
206 python_name: "getenv".to_string(),
207 rust_equiv: "std::env::var".to_string(),
208 requires_use: Some("std::env".to_string()),
209 wasm_compatible: WasmCompatibility::RequiresWasi,
210 transform_notes: None,
211 });
212 }
213
214 if !self.modules.contains_key("json") {
216 self.add_module(ModuleMapping {
217 python_module: "json".to_string(),
218 rust_crate: Some("serde_json".to_string()),
219 rust_use: "serde_json".to_string(),
220 dependencies: vec!["serde".to_string()],
221 version: "1.0".to_string(),
222 wasm_compatible: WasmCompatibility::Full,
223 notes: None,
224 });
225
226 self.add_function_mapping("json", FunctionMapping {
227 python_name: "loads".to_string(),
228 rust_equiv: "serde_json::from_str".to_string(),
229 requires_use: Some("serde_json".to_string()),
230 wasm_compatible: WasmCompatibility::Full,
231 transform_notes: None,
232 });
233
234 self.add_function_mapping("json", FunctionMapping {
235 python_name: "dumps".to_string(),
236 rust_equiv: "serde_json::to_string".to_string(),
237 requires_use: Some("serde_json".to_string()),
238 wasm_compatible: WasmCompatibility::Full,
239 transform_notes: None,
240 });
241 }
242 }
243
244 fn add_module(&mut self, mapping: ModuleMapping) {
245 self.modules.insert(mapping.python_module.clone(), mapping);
246 }
247
248 fn add_function_mapping(&mut self, module: &str, mapping: FunctionMapping) {
249 self.functions
250 .entry(module.to_string())
251 .or_insert_with(HashMap::new)
252 .insert(mapping.python_name.clone(), mapping);
253 }
254
255 pub fn get_module_mapping(&self, module: &str) -> Option<&ModuleMapping> {
256 self.modules.get(module)
257 }
258
259 pub fn get_function_mapping(&self, module: &str, function: &str) -> Option<&FunctionMapping> {
260 self.functions.get(module)?.get(function)
261 }
262
263 pub fn generate_use_statements(&self, modules: &[String]) -> Vec<String> {
264 modules
265 .iter()
266 .filter_map(|module| {
267 self.get_module_mapping(module)
268 .map(|mapping| format!("use {};", mapping.rust_use))
269 })
270 .collect()
271 }
272
273 pub fn generate_cargo_dependencies(&self, modules: &[String]) -> HashMap<String, String> {
274 let mut deps = HashMap::new();
275
276 for module in modules {
277 if let Some(mapping) = self.get_module_mapping(module) {
278 if let Some(crate_name) = &mapping.rust_crate {
279 deps.insert(crate_name.clone(), mapping.version.clone());
280 }
281
282 for dep in &mapping.dependencies {
283 deps.insert(dep.clone(), "*".to_string());
284 }
285 }
286 }
287
288 deps
289 }
290
291 pub fn get_wasm_compatibility(&self, module: &str) -> Option<WasmCompatibility> {
293 self.get_module_mapping(module)
294 .map(|m| m.wasm_compatible.clone())
295 }
296
297 pub fn get_all_modules(&self) -> Vec<&str> {
299 self.modules.keys().map(|s| s.as_str()).collect()
300 }
301
302 pub fn get_module(&self, module: &str) -> Option<&ModuleMapping> {
304 self.get_module_mapping(module)
305 }
306
307 pub fn get_function(&self, module: &str, function: &str) -> Option<String> {
308 self.get_function_mapping(module, function)
309 .map(|f| f.rust_equiv.clone())
310 }
311
312 pub fn collect_use_statements(&self, modules: &[String]) -> Vec<String> {
313 self.generate_use_statements(modules)
314 }
315
316 pub fn get_stats(&self) -> StdlibStats {
318 let total = self.modules.len();
319 let full_compat = self.modules.values()
320 .filter(|m| m.wasm_compatible == WasmCompatibility::Full)
321 .count();
322 let partial_compat = self.modules.values()
323 .filter(|m| m.wasm_compatible == WasmCompatibility::Partial)
324 .count();
325 let requires_wasi = self.modules.values()
326 .filter(|m| m.wasm_compatible == WasmCompatibility::RequiresWasi)
327 .count();
328 let requires_js = self.modules.values()
329 .filter(|m| m.wasm_compatible == WasmCompatibility::RequiresJsInterop)
330 .count();
331 let incompatible = self.modules.values()
332 .filter(|m| m.wasm_compatible == WasmCompatibility::Incompatible)
333 .count();
334
335 StdlibStats {
336 total_mapped: total,
337 full_wasm_compat: full_compat,
338 partial_wasm_compat: partial_compat,
339 requires_wasi,
340 requires_js_interop: requires_js,
341 incompatible,
342 }
343 }
344}
345
346#[derive(Debug)]
347pub struct StdlibStats {
348 pub total_mapped: usize,
349 pub full_wasm_compat: usize,
350 pub partial_wasm_compat: usize,
351 pub requires_wasi: usize,
352 pub requires_js_interop: usize,
353 pub incompatible: usize,
354}
355
356impl Default for StdlibMapper {
357 fn default() -> Self {
358 Self::new()
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365
366 #[test]
367 fn test_math_module_mapping() {
368 let mapper = StdlibMapper::new();
369 let math_mod = mapper.get_module_mapping("math");
370 assert!(math_mod.is_some());
371 assert_eq!(math_mod.unwrap().wasm_compatible, WasmCompatibility::Full);
372 }
373
374 #[test]
375 fn test_json_module_mapping() {
376 let mapper = StdlibMapper::new();
377 let json_mod = mapper.get_module_mapping("json");
378 assert!(json_mod.is_some());
379 assert_eq!(json_mod.unwrap().rust_crate, Some("serde_json".to_string()));
380 }
381
382 #[test]
383 fn test_function_mapping() {
384 let mapper = StdlibMapper::new();
385 let sqrt_func = mapper.get_function_mapping("math", "sqrt");
386 assert!(sqrt_func.is_some());
387 assert_eq!(sqrt_func.unwrap().rust_equiv, "f64::sqrt");
388 }
389
390 #[test]
391 fn test_cargo_dependencies() {
392 let mapper = StdlibMapper::new();
393 let deps = mapper.generate_cargo_dependencies(&vec!["json".to_string()]);
394 assert!(deps.contains_key("serde_json"));
395 }
396
397 #[test]
398 fn test_stats() {
399 let mapper = StdlibMapper::new();
400 let stats = mapper.get_stats();
401 assert!(stats.total_mapped > 0);
402 println!("Stdlib mapping stats: {:?}", stats);
403 }
404}