1use crate::python_ast::*;
7use crate::stdlib_mapper::StdlibMapper;
8use crate::{Error, Result};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
13pub struct TypeInference {
14 type_map: HashMap<String, RustType>,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum RustType {
21 I32,
22 I64,
23 F64,
24 Bool,
25 String,
26 Unit,
27 Vec(Box<RustType>),
28 Option(Box<RustType>),
29 Unknown,
30}
31
32impl RustType {
33 pub fn to_rust_str(&self) -> String {
35 match self {
36 RustType::I32 => "i32".to_string(),
37 RustType::I64 => "i64".to_string(),
38 RustType::F64 => "f64".to_string(),
39 RustType::Bool => "bool".to_string(),
40 RustType::String => "String".to_string(),
41 RustType::Unit => "()".to_string(),
42 RustType::Vec(inner) => format!("Vec<{}>", inner.to_rust_str()),
43 RustType::Option(inner) => format!("Option<{}>", inner.to_rust_str()),
44 RustType::Unknown => "()".to_string(),
45 }
46 }
47}
48
49impl TypeInference {
50 pub fn new() -> Self {
51 Self {
52 type_map: HashMap::new(),
53 }
54 }
55
56 pub fn infer_from_literal(&self, lit: &PyLiteral) -> RustType {
58 match lit {
59 PyLiteral::Int(n) => {
60 if *n >= i32::MIN as i64 && *n <= i32::MAX as i64 {
62 RustType::I32
63 } else {
64 RustType::I64
65 }
66 }
67 PyLiteral::Float(_) => RustType::F64,
68 PyLiteral::String(_) => RustType::String,
69 PyLiteral::Bool(_) => RustType::Bool,
70 PyLiteral::None => RustType::Option(Box::new(RustType::Unknown)),
71 PyLiteral::Bytes(_) => RustType::Vec(Box::new(RustType::I32)), }
73 }
74
75 pub fn infer_expr(&mut self, expr: &PyExpr) -> RustType {
77 match expr {
78 PyExpr::Literal(lit) => self.infer_from_literal(lit),
79 PyExpr::Name(name) => self
80 .type_map
81 .get(name)
82 .cloned()
83 .unwrap_or(RustType::Unknown),
84 PyExpr::BinOp { left, op, right } => {
85 let left_type = self.infer_expr(left);
86 let right_type = self.infer_expr(right);
87
88 match op {
90 BinOp::Add | BinOp::Sub | BinOp::Mult | BinOp::Mod => {
91 if left_type == RustType::F64 || right_type == RustType::F64 {
93 RustType::F64
94 } else if left_type == RustType::I64 || right_type == RustType::I64 {
95 RustType::I64
96 } else {
97 RustType::I32
98 }
99 }
100 BinOp::Div => RustType::F64, BinOp::FloorDiv => RustType::I32,
102 _ => RustType::Unknown,
103 }
104 }
105 PyExpr::UnaryOp { op, operand } => {
106 let operand_type = self.infer_expr(operand);
107 match op {
108 UnaryOp::Not => RustType::Bool,
109 UnaryOp::USub | UnaryOp::UAdd => operand_type,
110 _ => RustType::Unknown,
111 }
112 }
113 PyExpr::List(elements) => {
114 if elements.is_empty() {
115 RustType::Vec(Box::new(RustType::Unknown))
116 } else {
117 let first_type = self.infer_expr(&elements[0]);
118 RustType::Vec(Box::new(first_type))
119 }
120 }
121 PyExpr::Tuple(elements) => {
122 if elements.is_empty() {
123 RustType::Unknown
124 } else {
125 RustType::Unknown
128 }
129 }
130 PyExpr::Compare { .. } => {
131 RustType::Bool
133 }
134 PyExpr::Call { func, args, .. } => {
135 if let PyExpr::Attribute { value: _, attr } = func.as_ref() {
137 match attr.as_str() {
139 "split" | "splitlines" => RustType::Vec(Box::new(RustType::String)),
140 "strip" | "lstrip" | "rstrip" | "lower" | "upper" | "replace" => RustType::String,
141 "append" | "extend" | "remove" | "clear" | "sort" | "reverse" => {
142 RustType::Unknown }
144 "pop" => {
145 RustType::Unknown }
148 "join" => RustType::String,
149 "format" => RustType::String,
150 "items" | "keys" | "values" => RustType::Unknown, "count" | "index" | "find" => RustType::I32,
152 _ => RustType::Unknown,
153 }
154 } else if let PyExpr::Name(name) = func.as_ref() {
155 match name.as_str() {
157 "len" => RustType::I32,
158 "sum" => RustType::I32, "min" | "max" => {
160 if !args.is_empty() {
162 let arg_type = self.infer_expr(&args[0]);
163 if let RustType::Vec(inner) = arg_type {
164 *inner
165 } else {
166 arg_type }
168 } else {
169 RustType::I32
170 }
171 }
172 "abs" => {
173 if !args.is_empty() {
174 self.infer_expr(&args[0])
175 } else {
176 RustType::I32
177 }
178 }
179 "int" => RustType::I32,
180 "float" => RustType::F64,
181 "str" => RustType::String,
182 "bool" => RustType::Bool,
183 "range" => RustType::Unknown, "enumerate" | "zip" => RustType::Unknown, "sorted" | "reversed" => {
186 if !args.is_empty() {
188 self.infer_expr(&args[0])
189 } else {
190 RustType::Vec(Box::new(RustType::Unknown))
191 }
192 }
193 "list" => {
194 if !args.is_empty() {
196 if let RustType::Vec(inner) = self.infer_expr(&args[0]) {
197 RustType::Vec(inner)
198 } else {
199 RustType::Vec(Box::new(RustType::Unknown))
200 }
201 } else {
202 RustType::Vec(Box::new(RustType::Unknown))
203 }
204 }
205 "any" | "all" => RustType::Bool,
206 _ => RustType::Unknown,
207 }
208 } else {
209 RustType::Unknown
210 }
211 }
212 PyExpr::Attribute { value, attr } => {
213 match attr.as_str() {
215 "len" => RustType::I32,
216 "abs" => self.infer_expr(value),
217 "split" => RustType::Vec(Box::new(RustType::String)),
218 "strip" | "lower" | "upper" | "replace" => RustType::String,
219 "append" | "extend" | "remove" | "pop" | "clear" | "sort" | "reverse" => {
220 RustType::Unknown }
222 _ => RustType::Unknown,
223 }
224 }
225 _ => RustType::Unknown,
226 }
227 }
228
229 pub fn record_type(&mut self, name: String, rust_type: RustType) {
231 self.type_map.insert(name, rust_type);
232 }
233}
234
235impl Default for TypeInference {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241pub struct PythonToRustTranslator {
243 type_inference: TypeInference,
244 indent_level: usize,
245 stdlib_mapper: StdlibMapper,
246 imported_modules: Vec<String>,
247 module_aliases: HashMap<String, String>,
249}
250
251impl PythonToRustTranslator {
252 pub fn new() -> Self {
253 Self {
254 type_inference: TypeInference::new(),
255 indent_level: 0,
256 stdlib_mapper: StdlibMapper::new(),
257 imported_modules: Vec::new(),
258 module_aliases: HashMap::new(),
259 }
260 }
261
262 pub fn set_imports(&mut self, imports: Vec<String>) {
264 self.imported_modules = imports;
265 }
266
267 pub fn set_aliases(&mut self, aliases: HashMap<String, String>) {
269 self.module_aliases = aliases;
270 }
271
272 fn resolve_module_name(&self, name: &str) -> String {
274 self.module_aliases.get(name).cloned().unwrap_or_else(|| name.to_string())
275 }
276
277 fn extract_module_path(&self, expr: &PyExpr) -> Option<(String, String)> {
281 match expr {
282 PyExpr::Attribute { value, attr } => {
283 match value.as_ref() {
284 PyExpr::Name(name) => {
286 let resolved = self.resolve_module_name(name);
287 Some((resolved, attr.clone()))
288 }
289 PyExpr::Attribute { value: inner_value, attr: inner_attr } => {
291 if let PyExpr::Name(module_name) = inner_value.as_ref() {
292 let resolved = self.resolve_module_name(module_name);
293 let full_module = format!("{}.{}", resolved, inner_attr);
294 Some((full_module, attr.clone()))
295 } else {
296 None
297 }
298 }
299 _ => None,
300 }
301 }
302 _ => None,
303 }
304 }
305
306 fn indent(&self) -> String {
308 " ".repeat(self.indent_level)
309 }
310
311 pub fn translate_module(&mut self, module: &PyModule) -> Result<String> {
313 let mut code = String::new();
314
315 code.push_str("// Generated by Portalis Python → Rust Translator\n");
317 code.push_str("#![allow(unused)]\n\n");
318
319 for stmt in &module.statements {
321 code.push_str(&self.translate_stmt(stmt)?);
322 }
323
324 Ok(code)
325 }
326
327 pub fn translate_stmt(&mut self, stmt: &PyStmt) -> Result<String> {
329 match stmt {
330 PyStmt::Expr(expr) => {
331 let expr_code = self.translate_expr(expr)?;
332 Ok(format!("{}{};\n", self.indent(), expr_code))
333 }
334
335 PyStmt::Assign {
336 target,
337 value,
338 } => {
339 let value_code = self.translate_expr(value)?;
340
341 let rust_type = self.type_inference.infer_expr(value);
343 let type_str = rust_type.to_rust_str();
344
345 let target_name = match target {
347 PyExpr::Name(name) => name.clone(),
348 _ => {
349 return Err(Error::CodeGeneration(
350 "Complex assignment targets not yet supported".to_string()
351 ))
352 }
353 };
354
355 self.type_inference
357 .record_type(target_name.clone(), rust_type.clone());
358
359 Ok(format!(
360 "{}let {}: {} = {};\n",
361 self.indent(),
362 target_name,
363 type_str,
364 value_code
365 ))
366 }
367
368 PyStmt::AugAssign { target, op, value } => {
369 let target_code = self.translate_expr(target)?;
370 let value_code = self.translate_expr(value)?;
371 let op_str = self.binop_to_rust(*op);
372 Ok(format!(
373 "{}{} {}= {};\n",
374 self.indent(),
375 target_code,
376 op_str,
377 value_code
378 ))
379 }
380
381 PyStmt::FunctionDef {
382 name,
383 params,
384 body,
385 return_type,
386 decorators,
387 is_async,
388 } => {
389 let mut code = String::new();
390
391 for decorator in decorators {
393 let rust_attr = self.translate_decorator(decorator);
394 if !rust_attr.is_empty() {
395 code.push_str(&format!("{}{}\n", self.indent(), rust_attr));
396 }
397 }
398
399 let async_keyword = if *is_async { "async " } else { "" };
401 code.push_str(&format!("{}pub {}fn {}(", self.indent(), async_keyword, name));
402
403 let param_strs: Vec<String> = params
405 .iter()
406 .map(|param| {
407 let rust_type = if let Some(annotation) = ¶m.type_annotation {
408 self.type_annotation_to_rust(annotation)
409 } else {
410 RustType::Unknown
411 };
412 format!("{}: {}", param.name, rust_type.to_rust_str())
413 })
414 .collect();
415 code.push_str(¶m_strs.join(", "));
416
417 let ret_type = if let Some(annotation) = return_type {
419 self.type_annotation_to_rust(annotation)
420 } else {
421 RustType::Unit
422 };
423 code.push_str(&format!(") -> {} {{\n", ret_type.to_rust_str()));
424
425 self.indent_level += 1;
427 for stmt in body {
428 code.push_str(&self.translate_stmt(stmt)?);
429 }
430 self.indent_level -= 1;
431
432 code.push_str(&format!("{}}}\n\n", self.indent()));
433
434 Ok(code)
435 }
436
437 PyStmt::Return { value } => {
438 if let Some(e) = value {
439 let expr_code = self.translate_expr(e)?;
440 Ok(format!("{}return {};\n", self.indent(), expr_code))
441 } else {
442 Ok(format!("{}return;\n", self.indent()))
443 }
444 }
445
446 PyStmt::Assert { test, msg } => {
447 let test_code = self.translate_expr(test)?;
448 if let Some(msg_expr) = msg {
449 let msg_code = self.translate_expr(msg_expr)?;
450 Ok(format!("{}assert!({}, {});\n", self.indent(), test_code, msg_code))
451 } else {
452 Ok(format!("{}assert!({});\n", self.indent(), test_code))
453 }
454 }
455
456 PyStmt::If { test, body, orelse } => {
457 let mut code = String::new();
458 let test_code = self.translate_expr(test)?;
459
460 code.push_str(&format!("{}if {} {{\n", self.indent(), test_code));
461
462 self.indent_level += 1;
463 for stmt in body {
464 code.push_str(&self.translate_stmt(stmt)?);
465 }
466 self.indent_level -= 1;
467
468 code.push_str(&format!("{}}}", self.indent()));
469
470 if !orelse.is_empty() {
471 code.push_str(" else {\n");
472 self.indent_level += 1;
473 for stmt in orelse {
474 code.push_str(&self.translate_stmt(stmt)?);
475 }
476 self.indent_level -= 1;
477 code.push_str(&format!("{}}}", self.indent()));
478 }
479
480 code.push('\n');
481 Ok(code)
482 }
483
484 PyStmt::While { test, body, orelse } => {
485 let mut code = String::new();
486 let test_code = self.translate_expr(test)?;
487
488 if !orelse.is_empty() {
490 code.push_str(&format!("{}let mut _loop_completed = true;\n", self.indent()));
491 }
492
493 code.push_str(&format!("{}while {} {{\n", self.indent(), test_code));
494
495 self.indent_level += 1;
496 for stmt in body {
497 if !orelse.is_empty() && matches!(stmt, PyStmt::Break) {
499 code.push_str(&format!("{}_loop_completed = false;\n", self.indent()));
500 }
501 let stmt_code = self.translate_stmt(stmt)?;
502 code.push_str(&stmt_code);
503 }
504 self.indent_level -= 1;
505
506 code.push_str(&format!("{}}}\n", self.indent()));
507
508 if !orelse.is_empty() {
510 code.push_str(&format!("{}if _loop_completed {{\n", self.indent()));
511 self.indent_level += 1;
512 for stmt in orelse {
513 code.push_str(&self.translate_stmt(stmt)?);
514 }
515 self.indent_level -= 1;
516 code.push_str(&format!("{}}}\n", self.indent()));
517 }
518
519 Ok(code)
520 }
521
522 PyStmt::For { target, iter, body, orelse } => {
523 let mut code = String::new();
524 let rust_iter = self.translate_expr(iter)?;
526
527 let rust_target = match target {
529 PyExpr::Name(name) => {
530 self.type_inference.type_map.insert(name.clone(), RustType::I32);
532 name.clone()
533 }
534 _ => {
535 return Err(Error::CodeGeneration(
536 "Complex for loop targets not yet supported".to_string()
537 ))
538 }
539 };
540
541 if !orelse.is_empty() {
543 code.push_str(&format!("{}let mut _loop_completed = true;\n", self.indent()));
544 }
545
546 code.push_str(&format!("{}for {} in {} {{\n", self.indent(), rust_target, rust_iter));
547
548 self.indent_level += 1;
549 for stmt in body {
550 if !orelse.is_empty() && matches!(stmt, PyStmt::Break) {
552 code.push_str(&format!("{}_loop_completed = false;\n", self.indent()));
553 }
554 let stmt_code = self.translate_stmt(stmt)?;
555 code.push_str(&stmt_code);
556 }
557 self.indent_level -= 1;
558
559 code.push_str(&format!("{}}}\n", self.indent()));
560
561 if !orelse.is_empty() {
563 code.push_str(&format!("{}if _loop_completed {{\n", self.indent()));
564 self.indent_level += 1;
565 for stmt in orelse {
566 code.push_str(&self.translate_stmt(stmt)?);
567 }
568 self.indent_level -= 1;
569 code.push_str(&format!("{}}}\n", self.indent()));
570 }
571
572 Ok(code)
573 }
574
575 PyStmt::Pass => Ok(format!("{}// pass\n", self.indent())),
576
577 PyStmt::Break => Ok(format!("{}break;\n", self.indent())),
578
579 PyStmt::Continue => Ok(format!("{}continue;\n", self.indent())),
580
581 PyStmt::ClassDef {
582 name,
583 bases: _,
584 body,
585 decorators: _,
586 } => {
587 let mut code = String::new();
588
589 code.push_str(&format!("{}pub struct {} {{\n", self.indent(), name));
591
592 let mut attributes = vec![];
594 for stmt in body {
595 if let PyStmt::FunctionDef {
596 name: func_name,
597 body: func_body,
598 ..
599 } = stmt
600 {
601 if func_name == "__init__" {
602 for init_stmt in func_body {
604 if let PyStmt::Assign { target, value } = init_stmt {
605 if let PyExpr::Attribute { value: obj, attr } = target {
607 if let PyExpr::Name(name) = obj.as_ref() {
608 if name == "self" {
609 let attr_type = self.type_inference.infer_expr(value);
610 attributes.push((attr.clone(), attr_type));
611 }
612 }
613 }
614 }
615 }
616 }
617 }
618 }
619
620 self.indent_level += 1;
622 for (attr_name, attr_type) in &attributes {
623 code.push_str(&format!(
624 "{}pub {}: {},\n",
625 self.indent(),
626 attr_name,
627 attr_type.to_rust_str()
628 ));
629 }
630 self.indent_level -= 1;
631 code.push_str(&format!("{}}}\n\n", self.indent()));
632
633 code.push_str(&format!("{}impl {} {{\n", self.indent(), name));
635 self.indent_level += 1;
636
637 for stmt in body {
639 if let PyStmt::FunctionDef {
640 name: func_name,
641 params,
642 body: func_body,
643 return_type,
644 ..
645 } = stmt
646 {
647 if func_name == "__init__" {
649 code.push_str(&format!("{}pub fn new(", self.indent()));
651
652 let param_strs: Vec<String> = params
654 .iter()
655 .filter(|param| param.name != "self")
656 .map(|param| {
657 let rust_type = if let Some(annotation) = ¶m.type_annotation {
658 self.type_annotation_to_rust(annotation)
659 } else {
660 RustType::Unknown
661 };
662 format!("{}: {}", param.name, rust_type.to_rust_str())
663 })
664 .collect();
665 code.push_str(¶m_strs.join(", "));
666 code.push_str(&format!(") -> Self {{\n"));
667
668 self.indent_level += 1;
670 code.push_str(&format!("{}Self {{\n", self.indent()));
671 self.indent_level += 1;
672 for (attr_name, _) in &attributes {
673 code.push_str(&format!("{}{},\n", self.indent(), attr_name));
674 }
675 self.indent_level -= 1;
676 code.push_str(&format!("{}}}\n", self.indent()));
677 self.indent_level -= 1;
678 code.push_str(&format!("{}}}\n\n", self.indent()));
679 } else {
680 code.push_str(&format!("{}pub fn {}(", self.indent(), func_name));
682
683 let param_strs: Vec<String> = params
685 .iter()
686 .map(|param| {
687 if param.name == "self" {
688 "&self".to_string()
689 } else {
690 let rust_type = if let Some(annotation) = ¶m.type_annotation {
691 self.type_annotation_to_rust(annotation)
692 } else {
693 RustType::Unknown
694 };
695 format!("{}: {}", param.name, rust_type.to_rust_str())
696 }
697 })
698 .collect();
699 code.push_str(¶m_strs.join(", "));
700
701 let ret_type = if let Some(hint) = return_type {
703 self.type_annotation_to_rust(hint)
704 } else {
705 RustType::Unit
706 };
707 code.push_str(&format!(") -> {} {{\n", ret_type.to_rust_str()));
708
709 self.indent_level += 1;
711 for body_stmt in func_body {
712 code.push_str(&self.translate_stmt(body_stmt)?);
713 }
714 self.indent_level -= 1;
715 code.push_str(&format!("{}}}\n\n", self.indent()));
716 }
717 }
718 }
719
720 self.indent_level -= 1;
721 code.push_str(&format!("{}}}\n\n", self.indent()));
722
723 Ok(code)
724 }
725
726 PyStmt::Try { body, handlers, orelse, finalbody } => {
727 let mut code = String::new();
728
729 if !finalbody.is_empty() {
734 code.push_str(&format!("{}// try-except-finally\n", self.indent()));
736 code.push_str(&format!("{}{{\n", self.indent()));
737 self.indent_level += 1;
738
739 code.push_str(&format!("{}let _result = std::panic::catch_unwind(|| {{\n", self.indent()));
741 self.indent_level += 1;
742 for stmt in body {
743 code.push_str(&self.translate_stmt(stmt)?);
744 }
745 self.indent_level -= 1;
746 code.push_str(&format!("{}}});\n\n", self.indent()));
747
748 if !handlers.is_empty() {
750 code.push_str(&format!("{}if _result.is_err() {{\n", self.indent()));
751 self.indent_level += 1;
752 for handler in handlers {
753 for stmt in &handler.body {
754 code.push_str(&self.translate_stmt(stmt)?);
755 }
756 }
757 self.indent_level -= 1;
758 code.push_str(&format!("{}}}\n", self.indent()));
759 }
760
761 if !orelse.is_empty() {
763 code.push_str(&format!("{}if _result.is_ok() {{\n", self.indent()));
764 self.indent_level += 1;
765 for stmt in orelse {
766 code.push_str(&self.translate_stmt(stmt)?);
767 }
768 self.indent_level -= 1;
769 code.push_str(&format!("{}}}\n", self.indent()));
770 }
771
772 code.push_str(&format!("{}// finally\n", self.indent()));
774 for stmt in finalbody {
775 code.push_str(&self.translate_stmt(stmt)?);
776 }
777
778 self.indent_level -= 1;
779 code.push_str(&format!("{}}}\n", self.indent()));
780 } else {
781 code.push_str(&format!("{}// try-except\n", self.indent()));
783 for stmt in body {
784 code.push_str(&self.translate_stmt(stmt)?);
785 }
786
787 for (_i, handler) in handlers.iter().enumerate() {
789 if let Some(exc_type_expr) = &handler.exception_type {
790 let exc_type = self.translate_expr(exc_type_expr)?;
791 code.push_str(&format!("{}// except {}\n", self.indent(), exc_type));
792 } else {
793 code.push_str(&format!("{}// except (bare)\n", self.indent()));
794 }
795 }
796 }
797
798 Ok(code)
799 }
800
801 PyStmt::Raise { exception } => {
802 if let Some(e) = exception {
803 let exc_code = self.translate_expr(e)?;
804 Ok(format!("{}panic!(\"{{:?}}\", {});\n", self.indent(), exc_code))
805 } else {
806 Ok(format!("{}panic!(\"Exception raised\");\n", self.indent()))
807 }
808 }
809
810 PyStmt::Import { modules: _ } => {
811 Ok(String::new())
814 }
815
816 PyStmt::ImportFrom { module: _, names: _, level: _ } => {
817 Ok(String::new())
820 }
821
822 PyStmt::With { items, body } => {
823 let mut code = String::new();
824
825 for (idx, item) in items.iter().enumerate() {
830 let context_code = self.translate_expr(&item.context_expr)?;
831
832 let is_file_like = context_code.contains("open(")
834 || context_code.contains("File::open")
835 || context_code.contains("File::create");
836
837 if let Some(var) = &item.optional_vars {
838 let var_name = self.translate_expr(var)?;
839 if is_file_like {
840 code.push_str(&format!(
842 "{}let mut {} = {};\n",
843 self.indent(),
844 var_name,
845 context_code
846 ));
847 } else {
848 code.push_str(&format!(
850 "{}let {} = {};\n",
851 self.indent(),
852 var_name,
853 context_code
854 ));
855 }
856 } else {
857 code.push_str(&format!(
859 "{}{};\n",
860 self.indent(),
861 context_code
862 ));
863 }
864
865 if idx == items.len() - 1 {
867 code.push_str(&format!("{}{{\n", self.indent()));
868 }
869 }
870
871 self.indent_level += 1;
873 for stmt in body {
874 code.push_str(&self.translate_stmt(stmt)?);
875 }
876 self.indent_level -= 1;
877
878 code.push_str(&format!("{}}}\n", self.indent()));
880 code.push_str(&format!("{}// End of with block\n", self.indent()));
881
882 Ok(code)
883 }
884
885 _ => Err(Error::CodeGeneration(format!(
886 "Statement type not yet implemented: {:?}",
887 stmt
888 ))),
889 }
890 }
891
892 pub fn translate_expr(&mut self, expr: &PyExpr) -> Result<String> {
894 match expr {
895 PyExpr::Literal(lit) => self.translate_literal(lit),
896
897 PyExpr::Name(name) => Ok(name.clone()),
898
899 PyExpr::Await(value) => {
900 let value_code = self.translate_expr(value)?;
901 Ok(format!("{}.await", value_code))
902 }
903
904 PyExpr::BinOp { left, op, right } => {
905 let left_code = self.translate_expr(left)?;
906 let right_code = self.translate_expr(right)?;
907 let op_str = self.binop_to_rust(*op);
908 Ok(format!("{} {} {}", left_code, op_str, right_code))
909 }
910
911 PyExpr::UnaryOp { op, operand } => {
912 let operand_code = self.translate_expr(operand)?;
913 let op_str = self.unaryop_to_rust(*op);
914 Ok(format!("{}{}", op_str, operand_code))
915 }
916
917 PyExpr::Call { func, args, .. } => {
918 if let PyExpr::Attribute { value, attr } = func.as_ref() {
920 if let Some((module_path, final_attr)) = self.extract_module_path(func) {
922 let parts: Vec<&str> = module_path.split('.').collect();
923 let base_module = parts[0];
924
925 if self.imported_modules.contains(&module_path) || self.imported_modules.contains(&base_module.to_string()) {
926 let args_code: Vec<String> = args
927 .iter()
928 .map(|arg| self.translate_expr(arg))
929 .collect::<Result<Vec<_>>>()?;
930
931 if module_path == "math" && final_attr == "sqrt" {
933 if !args_code.is_empty() {
934 return Ok(format!("({} as f64).sqrt()", args_code[0]));
935 }
936 }
937 if module_path == "math" && final_attr == "pow" {
938 if args_code.len() >= 2 {
939 return Ok(format!("({} as f64).powf({} as f64)", args_code[0], args_code[1]));
940 }
941 }
942 if module_path == "json" && final_attr == "dumps" {
943 if !args_code.is_empty() {
944 return Ok(format!("serde_json::to_string(&{})?", args_code[0]));
945 }
946 }
947 if module_path == "json" && final_attr == "loads" {
948 if !args_code.is_empty() {
949 return Ok(format!("serde_json::from_str({})?", args_code[0]));
950 }
951 }
952 if module_path == "os.path" && final_attr == "exists" {
954 if !args_code.is_empty() {
955 return Ok(format!("std::path::Path::new(&{}).exists()", args_code[0]));
956 }
957 }
958 if module_path == "os.path" && final_attr == "join" {
959 if args_code.len() >= 2 {
960 return Ok(format!("std::path::Path::new(&{}).join({})", args_code[0], args_code[1]));
961 }
962 }
963 }
964 }
965
966 let value_code = self.translate_expr(value)?;
967 let args_code: Vec<String> = args
968 .iter()
969 .map(|arg| self.translate_expr(arg))
970 .collect::<Result<Vec<_>>>()?;
971
972 match attr.as_str() {
974 "upper" => return Ok(format!("{}.to_uppercase()", value_code)),
975 "lower" => return Ok(format!("{}.to_lowercase()", value_code)),
976 "strip" => return Ok(format!("{}.trim()", value_code)),
977 "lstrip" => return Ok(format!("{}.trim_start()", value_code)),
978 "rstrip" => return Ok(format!("{}.trim_end()", value_code)),
979 "split" => {
980 if args_code.is_empty() {
981 return Ok(format!("{}.split_whitespace().collect::<Vec<_>>()", value_code));
982 } else {
983 return Ok(format!("{}.split({}).collect::<Vec<_>>()", value_code, args_code[0]));
984 }
985 }
986 "join" => {
987 if args_code.len() == 1 {
988 return Ok(format!("{}.join(&{})", args_code[0], value_code));
989 }
990 }
991 "replace" => {
992 if args_code.len() == 2 {
993 return Ok(format!("{}.replace({}, {})", value_code, args_code[0], args_code[1]));
994 }
995 }
996 "startswith" => {
997 if args_code.len() == 1 {
998 return Ok(format!("{}.starts_with({})", value_code, args_code[0]));
999 }
1000 }
1001 "endswith" => {
1002 if args_code.len() == 1 {
1003 return Ok(format!("{}.ends_with({})", value_code, args_code[0]));
1004 }
1005 }
1006 "find" => {
1007 if args_code.len() == 1 {
1008 return Ok(format!("{}.find({}).unwrap_or(-1_isize) as i32", value_code, args_code[0]));
1009 }
1010 }
1011 "count" => {
1012 if args_code.len() == 1 {
1013 return Ok(format!("{}.matches({}).count()", value_code, args_code[0]));
1014 }
1015 }
1016 "append" => {
1018 if args_code.len() == 1 {
1019 return Ok(format!("{}.push({})", value_code, args_code[0]));
1020 }
1021 }
1022 "extend" => {
1023 if args_code.len() == 1 {
1024 return Ok(format!("{}.extend({})", value_code, args_code[0]));
1025 }
1026 }
1027 "pop" => {
1028 if args_code.is_empty() {
1029 return Ok(format!("{}.pop().unwrap()", value_code));
1030 } else {
1031 return Ok(format!("{}.remove({} as usize)", value_code, args_code[0]));
1032 }
1033 }
1034 "remove" => {
1035 if args_code.len() == 1 {
1036 return Ok(format!("{{ if let Some(pos) = {}.iter().position(|x| x == &{}) {{ {}.remove(pos); }} }}",
1037 value_code, args_code[0], value_code));
1038 }
1039 }
1040 "clear" => return Ok(format!("{}.clear()", value_code)),
1041 "reverse" => return Ok(format!("{}.reverse()", value_code)),
1042 "sort" => return Ok(format!("{}.sort()", value_code)),
1043 _ => {
1044 return Ok(format!("{}.{}({})", value_code, attr, args_code.join(", ")));
1046 }
1047 }
1048 }
1049
1050 if let PyExpr::Name(func_name) = func.as_ref() {
1052 let args_code: Vec<String> = args
1053 .iter()
1054 .map(|arg| self.translate_expr(arg))
1055 .collect::<Result<Vec<_>>>()?;
1056
1057 match func_name.as_str() {
1058 "len" => {
1060 if args_code.len() == 1 {
1061 return Ok(format!("{}.len()", args_code[0]));
1062 }
1063 }
1064 "max" => {
1065 if args_code.len() == 1 {
1066 return Ok(format!("*{}.iter().max().unwrap()", args_code[0]));
1068 } else if args_code.len() > 1 {
1069 return Ok(format!("*[{}].iter().max().unwrap()", args_code.join(", ")));
1071 }
1072 }
1073 "min" => {
1074 if args_code.len() == 1 {
1075 return Ok(format!("*{}.iter().min().unwrap()", args_code[0]));
1076 } else if args_code.len() > 1 {
1077 return Ok(format!("*[{}].iter().min().unwrap()", args_code.join(", ")));
1078 }
1079 }
1080 "sum" => {
1081 if args_code.len() == 1 {
1082 return Ok(format!("{}.iter().sum::<i32>()", args_code[0]));
1083 }
1084 }
1085 "abs" => {
1086 if args_code.len() == 1 {
1087 return Ok(format!("{}.abs()", args_code[0]));
1088 }
1089 }
1090 "sorted" => {
1091 if args_code.len() == 1 {
1092 return Ok(format!("{{ let mut v = {}.clone(); v.sort(); v }}", args_code[0]));
1093 }
1094 }
1095 "reversed" => {
1096 if args_code.len() == 1 {
1097 return Ok(format!("{{ let mut v = {}.clone(); v.reverse(); v }}", args_code[0]));
1098 }
1099 }
1100 "print" => {
1101 return Ok(format!("println!(\"{{:?}}\", {})", args_code.join(", ")));
1102 }
1103 "range" => {
1104 if args_code.len() == 1 {
1107 return Ok(format!("0..{}", args_code[0]));
1108 } else if args_code.len() == 2 {
1109 return Ok(format!("{}..{}", args_code[0], args_code[1]));
1110 } else if args_code.len() == 3 {
1111 return Ok(format!("({}..{}).step_by({} as usize)", args_code[0], args_code[1], args_code[2]));
1113 }
1114 }
1115 "enumerate" => {
1116 if args_code.len() == 1 {
1117 return Ok(format!("{}.iter().enumerate()", args_code[0]));
1118 }
1119 }
1120 "zip" => {
1121 if args_code.len() == 2 {
1122 return Ok(format!("{}.iter().zip({}.iter())", args_code[0], args_code[1]));
1123 } else if args_code.len() > 2 {
1124 let mut result = format!("{}.iter().zip({}.iter())", args_code[0], args_code[1]);
1126 for arg in &args_code[2..] {
1127 result = format!("{}.zip({}.iter())", result, arg);
1128 }
1129 return Ok(result);
1130 }
1131 }
1132 "any" => {
1133 if args_code.len() == 1 {
1134 return Ok(format!("{}.iter().any(|x| *x)", args_code[0]));
1135 }
1136 }
1137 "all" => {
1138 if args_code.len() == 1 {
1139 return Ok(format!("{}.iter().all(|x| *x)", args_code[0]));
1140 }
1141 }
1142 "isinstance" => {
1143 return Ok(format!("/* isinstance({}, {}) */true", args_code.join(", "), ""));
1145 }
1146 "type" => {
1147 if args_code.len() == 1 {
1148 return Ok(format!("/* type({}) */", args_code[0]));
1149 }
1150 }
1151 "list" => {
1152 if args_code.len() == 1 {
1154 if args_code[0].contains(".iter()") || args_code[0].contains("..") {
1156 return Ok(format!("{}.collect::<Vec<_>>()", args_code[0]));
1157 } else {
1158 return Ok(format!("{}.iter().collect::<Vec<_>>()", args_code[0]));
1159 }
1160 } else if args_code.is_empty() {
1161 return Ok("Vec::new()".to_string());
1162 }
1163 }
1164 "dict" => {
1165 if args_code.is_empty() {
1166 return Ok("HashMap::new()".to_string());
1167 }
1168 }
1169 "str" => {
1170 if args_code.len() == 1 {
1171 return Ok(format!("{}.to_string()", args_code[0]));
1172 }
1173 }
1174 "int" => {
1175 if args_code.len() == 1 {
1176 return Ok(format!("({} as i32)", args_code[0]));
1177 }
1178 }
1179 "float" => {
1180 if args_code.len() == 1 {
1181 return Ok(format!("({} as f64)", args_code[0]));
1182 }
1183 }
1184 "bool" => {
1185 if args_code.len() == 1 {
1186 return Ok(format!("({} as bool)", args_code[0]));
1187 }
1188 }
1189 _ => {
1190 return Ok(format!("{}({})", func_name, args_code.join(", ")));
1192 }
1193 }
1194 }
1195
1196 let func_code = self.translate_expr(func)?;
1198 let args_code: Vec<String> = args
1199 .iter()
1200 .map(|arg| self.translate_expr(arg))
1201 .collect::<Result<Vec<_>>>()?;
1202 Ok(format!("{}({})", func_code, args_code.join(", ")))
1203 }
1204
1205 PyExpr::List(elements) => {
1206 let elements_code: Vec<String> = elements
1207 .iter()
1208 .map(|e| self.translate_expr(e))
1209 .collect::<Result<Vec<_>>>()?;
1210 Ok(format!("vec![{}]", elements_code.join(", ")))
1211 }
1212
1213 PyExpr::Tuple(elements) => {
1214 let elements_code: Vec<String> = elements
1215 .iter()
1216 .map(|e| self.translate_expr(e))
1217 .collect::<Result<Vec<_>>>()?;
1218 Ok(format!("({})", elements_code.join(", ")))
1219 }
1220
1221 PyExpr::Compare { left, op, right } => {
1222 let left_code = self.translate_expr(left)?;
1223 let right_code = self.translate_expr(right)?;
1224 let op_str = self.cmpop_to_rust(*op);
1225 Ok(format!("{} {} {}", left_code, op_str, right_code))
1226 }
1227
1228 PyExpr::Subscript { value, index } => {
1229 let value_code = self.translate_expr(value)?;
1230 let index_code = self.translate_expr(index)?;
1231 Ok(format!("{}[{}]", value_code, index_code))
1232 }
1233
1234 PyExpr::Slice { value, lower, upper, step } => {
1235 let value_code = self.translate_expr(value)?;
1236
1237 if step.is_some() {
1245 let step_code = self.translate_expr(step.as_ref().unwrap())?;
1247
1248 let range_str = if lower.is_some() && upper.is_some() {
1249 let lower_code = self.translate_expr(lower.as_ref().unwrap())?;
1250 let upper_code = self.translate_expr(upper.as_ref().unwrap())?;
1251 format!("{}..{}", lower_code, upper_code)
1252 } else if lower.is_some() {
1253 let lower_code = self.translate_expr(lower.as_ref().unwrap())?;
1254 format!("{}..", lower_code)
1255 } else if upper.is_some() {
1256 let upper_code = self.translate_expr(upper.as_ref().unwrap())?;
1257 format!("..{}", upper_code)
1258 } else {
1259 "..".to_string()
1260 };
1261
1262 Ok(format!(
1263 "{}[{}].iter().step_by({} as usize).copied().collect::<Vec<_>>()",
1264 value_code, range_str, step_code
1265 ))
1266 } else {
1267 let slice_str = if lower.is_some() && upper.is_some() {
1269 let lower_code = self.translate_expr(lower.as_ref().unwrap())?;
1270 let upper_code = self.translate_expr(upper.as_ref().unwrap())?;
1271 format!("{}..{}", lower_code, upper_code)
1272 } else if lower.is_some() {
1273 let lower_code = self.translate_expr(lower.as_ref().unwrap())?;
1274 format!("{}..", lower_code)
1275 } else if upper.is_some() {
1276 let upper_code = self.translate_expr(upper.as_ref().unwrap())?;
1277 format!("..{}", upper_code)
1278 } else {
1279 "..".to_string()
1280 };
1281
1282 Ok(format!("&{}[{}]", value_code, slice_str))
1283 }
1284 }
1285
1286 PyExpr::Attribute { value, attr } => {
1287 if let Some((module_path, final_attr)) = self.extract_module_path(&PyExpr::Attribute {
1289 value: value.clone(),
1290 attr: attr.clone()
1291 }) {
1292 let parts: Vec<&str> = module_path.split('.').collect();
1294 let base_module = parts[0];
1295
1296 if self.imported_modules.contains(&module_path) || self.imported_modules.contains(&base_module.to_string()) {
1297 if let Some(rust_equiv) = self.stdlib_mapper.get_function(&module_path, &final_attr) {
1299 if rust_equiv.contains("::") {
1301 return Ok(rust_equiv.clone());
1302 }
1303 }
1304
1305 if let Some(_mapping) = self.stdlib_mapper.get_module(&module_path) {
1307 if module_path == "math" && final_attr == "pi" {
1309 return Ok("std::f64::consts::PI".to_string());
1310 }
1311 if module_path == "math" && final_attr == "e" {
1312 return Ok("std::f64::consts::E".to_string());
1313 }
1314 }
1315 }
1316 }
1317
1318 let value_code = self.translate_expr(value)?;
1320 Ok(format!("{}.{}", value_code, attr))
1321 }
1322
1323 PyExpr::Dict { keys, values } => {
1324 if keys.is_empty() {
1325 return Ok("HashMap::new()".to_string());
1326 }
1327
1328 let mut pairs = vec![];
1329 for (key, value) in keys.iter().zip(values.iter()) {
1330 let key_code = self.translate_expr(key)?;
1331 let value_code = self.translate_expr(value)?;
1332 pairs.push(format!("({}, {})", key_code, value_code));
1333 }
1334
1335 Ok(format!(
1336 "HashMap::from([{}])",
1337 pairs.join(", ")
1338 ))
1339 }
1340
1341 PyExpr::ListComp { element, generators } => {
1342 if generators.is_empty() {
1344 return Err(Error::CodeGeneration("List comprehension with no generators".to_string()));
1345 }
1346
1347 let comp = &generators[0];
1348 let iter_code = self.translate_expr(&comp.iter)?;
1349 let element_code = self.translate_expr(element)?;
1350
1351 let rust_iter = if iter_code.starts_with("range(") {
1353 let args = &iter_code[6..iter_code.len() - 1];
1354 if args.contains(',') {
1355 let parts: Vec<&str> = args.split(',').collect();
1356 format!("({}..{})", parts[0].trim(), parts[1].trim())
1357 } else {
1358 format!("(0..{})", args)
1359 }
1360 } else {
1361 iter_code
1362 };
1363
1364 let target_var = self.translate_expr(&comp.target)?;
1366 let mut code = format!("{}.map(|{}| {})", rust_iter, target_var, element_code);
1367
1368 if !comp.ifs.is_empty() {
1370 for condition in &comp.ifs {
1371 let condition_code = self.translate_expr(condition)?;
1372 code = format!("{}.filter(|{}| {})", code, target_var, condition_code);
1373 }
1374 }
1375
1376 code = format!("{}.collect::<Vec<_>>()", code);
1378
1379 Ok(code)
1380 }
1381
1382 PyExpr::Lambda { args, body } => {
1383 let args_str = args.join(", ");
1388 let body_code = self.translate_expr(body)?;
1389
1390 Ok(format!("|{}| {}", args_str, body_code))
1391 }
1392
1393 _ => Err(Error::CodeGeneration(format!(
1394 "Expression type not yet implemented: {:?}",
1395 expr
1396 ))),
1397 }
1398 }
1399
1400 fn cmpop_to_rust(&self, op: CmpOp) -> &str {
1402 match op {
1403 CmpOp::Eq => "==",
1404 CmpOp::NotEq => "!=",
1405 CmpOp::Lt => "<",
1406 CmpOp::LtE => "<=",
1407 CmpOp::Gt => ">",
1408 CmpOp::GtE => ">=",
1409 CmpOp::Is => "==", CmpOp::IsNot => "!=", CmpOp::In => "contains", CmpOp::NotIn => "!contains", }
1414 }
1415
1416 fn translate_literal(&self, lit: &PyLiteral) -> Result<String> {
1418 match lit {
1419 PyLiteral::Int(n) => Ok(n.to_string()),
1420 PyLiteral::Float(f) => Ok(f.to_string()),
1421 PyLiteral::String(s) => Ok(format!("\"{}\"", s)),
1422 PyLiteral::Bool(b) => Ok(b.to_string()),
1423 PyLiteral::None => Ok("None".to_string()), PyLiteral::Bytes(_) => Ok("vec![]".to_string()), }
1426 }
1427
1428 fn binop_to_rust(&self, op: BinOp) -> &str {
1430 match op {
1431 BinOp::Add => "+",
1432 BinOp::Sub => "-",
1433 BinOp::Mult => "*",
1434 BinOp::Div => "/",
1435 BinOp::FloorDiv => "/", BinOp::Mod => "%",
1437 BinOp::Pow => "pow", BinOp::LShift => "<<",
1439 BinOp::RShift => ">>",
1440 BinOp::BitOr => "|",
1441 BinOp::BitXor => "^",
1442 BinOp::BitAnd => "&",
1443 BinOp::MatMult => "*", }
1445 }
1446
1447 fn unaryop_to_rust(&self, op: UnaryOp) -> &str {
1449 match op {
1450 UnaryOp::Invert => "!",
1451 UnaryOp::Not => "!",
1452 UnaryOp::UAdd => "+",
1453 UnaryOp::USub => "-",
1454 }
1455 }
1456
1457 fn type_annotation_to_rust(&self, annotation: &TypeAnnotation) -> RustType {
1459 match annotation {
1460 TypeAnnotation::Name(name) => self.python_type_to_rust(name),
1461 TypeAnnotation::Generic { base, args } => {
1462 let base_type = if let TypeAnnotation::Name(name) = base.as_ref() {
1464 name.as_str()
1465 } else {
1466 return RustType::Unknown;
1467 };
1468
1469 match base_type {
1470 "List" | "list" => {
1471 if let Some(inner) = args.first() {
1472 let inner_type = self.type_annotation_to_rust(inner);
1473 RustType::Vec(Box::new(inner_type))
1474 } else {
1475 RustType::Vec(Box::new(RustType::Unknown))
1476 }
1477 }
1478 "Optional" | "Option" => {
1479 if let Some(inner) = args.first() {
1480 let inner_type = self.type_annotation_to_rust(inner);
1481 RustType::Option(Box::new(inner_type))
1482 } else {
1483 RustType::Option(Box::new(RustType::Unknown))
1484 }
1485 }
1486 _ => RustType::Unknown,
1487 }
1488 }
1489 }
1490 }
1491
1492 fn python_type_to_rust(&self, hint: &str) -> RustType {
1494 match hint {
1495 "int" => RustType::I32,
1496 "float" => RustType::F64,
1497 "str" => RustType::String,
1498 "bool" => RustType::Bool,
1499 _ => RustType::Unknown,
1500 }
1501 }
1502
1503 fn translate_decorator(&self, decorator: &PyExpr) -> String {
1505 let decorator_name = match decorator {
1507 PyExpr::Name(name) => name.as_str(),
1508 PyExpr::Call { func, .. } => {
1509 if let PyExpr::Name(name) = func.as_ref() {
1511 name.as_str()
1512 } else {
1513 return String::new();
1514 }
1515 }
1516 _ => return String::new(),
1517 };
1518
1519 match decorator_name {
1520 "staticmethod" => "#[allow(non_snake_case)]".to_string(),
1522 "classmethod" => "#[allow(non_snake_case)]".to_string(),
1523 "property" => "#[inline]".to_string(),
1524 "abstractmethod" => "".to_string(), "dataclass" => "#[derive(Debug, Clone)]".to_string(),
1526 "lru_cache" | "cache" => "// TODO: Add caching".to_string(),
1527 "override" => "#[inline]".to_string(),
1528 "deprecated" => "#[deprecated]".to_string(),
1529 "async" | "asyncio.coroutine" => "#[tokio::main]".to_string(),
1530 "pytest.fixture" => "#[test]".to_string(),
1531 "unittest.mock.patch" => "// Mock decorator".to_string(),
1532 _ => {
1533 format!("// @{}", decorator_name)
1535 }
1536 }
1537 }
1538}
1539
1540impl Default for PythonToRustTranslator {
1541 fn default() -> Self {
1542 Self::new()
1543 }
1544}
1545
1546#[cfg(test)]
1547mod tests {
1548 use super::*;
1549
1550 #[test]
1551 fn test_translate_simple_assignment() {
1552 let mut translator = PythonToRustTranslator::new();
1553
1554 let module = PyModule {
1555 statements: vec![PyStmt::Assign {
1556 target: PyExpr::Name("x".to_string()),
1557 value: PyExpr::Literal(PyLiteral::Int(42)),
1558 }],
1559 };
1560
1561 let result = translator.translate_module(&module).unwrap();
1562 assert!(result.contains("let x: i32 = 42;"));
1563 }
1564
1565 #[test]
1566 fn test_translate_function() {
1567 let mut translator = PythonToRustTranslator::new();
1568
1569 let module = PyModule {
1570 statements: vec![PyStmt::FunctionDef {
1571 name: "add".to_string(),
1572 params: vec![
1573 FunctionParam {
1574 name: "a".to_string(),
1575 type_annotation: Some(TypeAnnotation::Name("int".to_string())),
1576 default_value: None,
1577 },
1578 FunctionParam {
1579 name: "b".to_string(),
1580 type_annotation: Some(TypeAnnotation::Name("int".to_string())),
1581 default_value: None,
1582 },
1583 ],
1584 body: vec![PyStmt::Return {
1585 value: Some(PyExpr::BinOp {
1586 left: Box::new(PyExpr::Name("a".to_string())),
1587 op: BinOp::Add,
1588 right: Box::new(PyExpr::Name("b".to_string())),
1589 })
1590 }],
1591 return_type: Some(TypeAnnotation::Name("int".to_string())),
1592 decorators: vec![],
1593 is_async: false,
1594 }],
1595 };
1596
1597 let result = translator.translate_module(&module).unwrap();
1598 assert!(result.contains("pub fn add(a: i32, b: i32) -> i32"));
1599 assert!(result.contains("return a + b;"));
1600 }
1601
1602 #[test]
1603 fn test_type_inference_int() {
1604 let inference = TypeInference::new();
1605 let lit = PyLiteral::Int(42);
1606 assert_eq!(inference.infer_from_literal(&lit), RustType::I32);
1607 }
1608
1609 #[test]
1610 fn test_type_inference_float() {
1611 let inference = TypeInference::new();
1612 let lit = PyLiteral::Float(3.14);
1613 assert_eq!(inference.infer_from_literal(&lit), RustType::F64);
1614 }
1615
1616 #[test]
1617 fn test_type_inference_string() {
1618 let inference = TypeInference::new();
1619 let lit = PyLiteral::String("hello".to_string());
1620 assert_eq!(inference.infer_from_literal(&lit), RustType::String);
1621 }
1622}