1mod code_generator;
6pub mod class_translator;
7pub mod class_inheritance;
8pub mod decorator_translator;
9pub mod generator_translator;
10pub mod asyncio_orchestrator;
11pub mod threading_translator;
12pub mod type_inference;
13pub mod lifetime_analysis;
14pub mod generic_translator;
15pub mod reference_optimizer;
16pub mod version_resolver;
17pub mod numpy_translator;
18pub mod pandas_translator;
19pub mod common_libraries_translator;
20pub mod python_ast;
21pub mod python_to_rust;
22pub mod python_parser;
23pub mod expression_translator;
24pub mod statement_translator;
25pub mod simple_parser;
26pub mod indented_parser;
27pub mod feature_translator;
28pub mod stdlib_mapper;
29pub mod stdlib_mappings_comprehensive;
30pub mod wasi_core;
31pub mod wasi_directory;
32pub mod wasi_fs;
33pub mod wasi_filesystem;
34pub mod wasi_fetch;
35pub mod wasi_websocket;
36pub mod wasi_threading;
37pub mod wasi_async_runtime;
38pub mod web_workers;
39pub mod py_to_rust_fs;
40pub mod py_to_rust_http;
41pub mod py_to_rust_asyncio;
42pub mod import_analyzer;
43pub mod cargo_generator;
44pub mod external_packages;
45pub mod dependency_graph;
46pub mod dependency_resolver;
47pub mod version_compatibility;
48pub mod build_optimizer;
49pub mod dead_code_eliminator;
50pub mod code_splitter;
51pub mod wasm_bundler;
52pub mod npm_package_generator;
53pub mod typescript_generator;
54pub mod multi_target_builder;
55pub mod advanced_features;
56
57#[cfg(target_arch = "wasm32")]
59pub mod wasm;
60
61#[cfg(test)]
62mod test_import_aliases;
63
64#[cfg(test)]
65mod day3_features_test;
66
67#[cfg(test)]
68mod day4_5_features_test;
69
70#[cfg(test)]
71mod day6_7_features_test;
72
73#[cfg(test)]
74mod day8_9_features_test;
75
76#[cfg(test)]
77mod day10_11_features_test;
78
79#[cfg(test)]
80mod day12_13_features_test;
81
82#[cfg(test)]
83mod day14_15_features_test;
84
85#[cfg(test)]
86mod day16_17_features_test;
87
88#[cfg(test)]
89mod day18_19_features_test;
90
91#[cfg(test)]
92mod day20_21_features_test;
93
94#[cfg(test)]
95mod day22_23_features_test;
96
97#[cfg(test)]
98mod day24_25_features_test;
99
100#[cfg(test)]
101mod day26_27_features_test;
102
103#[cfg(test)]
104mod day28_29_features_test;
105
106#[cfg(test)]
107mod day30_features_test;
108
109#[cfg(not(target_arch = "wasm32"))]
110use async_trait::async_trait;
111
112use code_generator::CodeGenerator;
113pub use class_translator::ClassTranslator;
114use python_parser::PythonParser;
115use python_to_rust::PythonToRustTranslator;
116use feature_translator::FeatureTranslator;
117
118#[cfg(not(target_arch = "wasm32"))]
119use portalis_core::{Agent, AgentCapability, AgentId, ArtifactMetadata, Error, Result};
120
121#[cfg(target_arch = "wasm32")]
122pub type Result<T> = std::result::Result<T, Error>;
123
124#[cfg(target_arch = "wasm32")]
125#[derive(Debug)]
126pub enum Error {
127 CodeGeneration(String),
128 Parse(String),
129 Other(String),
130}
131
132#[cfg(target_arch = "wasm32")]
133impl std::fmt::Display for Error {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 match self {
136 Error::CodeGeneration(msg) => write!(f, "Code generation error: {}", msg),
137 Error::Parse(msg) => write!(f, "Parse error: {}", msg),
138 Error::Other(msg) => write!(f, "Error: {}", msg),
139 }
140 }
141}
142
143#[cfg(target_arch = "wasm32")]
144impl std::error::Error for Error {}
145
146#[cfg(target_arch = "wasm32")]
147impl From<String> for Error {
148 fn from(s: String) -> Self {
149 Error::Other(s)
150 }
151}
152
153#[cfg(target_arch = "wasm32")]
154impl From<&str> for Error {
155 fn from(s: &str) -> Self {
156 Error::Other(s.to_string())
157 }
158}
159
160use serde::{Deserialize, Serialize};
161
162#[cfg(all(feature = "nemo", not(target_arch = "wasm32")))]
163use portalis_nemo_bridge::{NeMoClient, TranslateRequest};
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct TranspilerInput {
168 pub typed_functions: Vec<serde_json::Value>,
169 #[serde(default)]
170 pub typed_classes: Vec<serde_json::Value>,
171 #[serde(default)]
172 pub use_statements: Vec<String>,
173 #[serde(default)]
174 pub cargo_dependencies: Vec<serde_json::Value>,
175 pub api_contract: serde_json::Value,
176}
177
178#[cfg(not(target_arch = "wasm32"))]
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct TranspilerOutput {
182 pub rust_code: String,
183 pub metadata: ArtifactMetadata,
184}
185
186#[cfg(not(target_arch = "wasm32"))]
188#[derive(Debug, Clone)]
189pub enum TranslationMode {
190 PatternBased,
193 AstBased,
196 FeatureBased,
199 #[cfg(feature = "nemo")]
201 NeMo {
202 service_url: String,
203 mode: String,
204 temperature: f32,
205 },
206}
207
208#[cfg(not(target_arch = "wasm32"))]
209impl Default for TranslationMode {
210 fn default() -> Self {
211 Self::PatternBased
212 }
213}
214
215#[cfg(not(target_arch = "wasm32"))]
217pub struct TranspilerAgent {
218 id: AgentId,
219 translation_mode: TranslationMode,
220}
221
222#[cfg(not(target_arch = "wasm32"))]
223impl TranspilerAgent {
224 pub fn new() -> Self {
225 Self {
226 id: AgentId::new(),
227 translation_mode: TranslationMode::default(),
228 }
229 }
230
231 pub fn with_mode(translation_mode: TranslationMode) -> Self {
233 Self {
234 id: AgentId::new(),
235 translation_mode,
236 }
237 }
238
239 pub fn with_ast_mode() -> Self {
241 Self::with_mode(TranslationMode::AstBased)
242 }
243
244 pub fn with_feature_mode() -> Self {
246 Self::with_mode(TranslationMode::FeatureBased)
247 }
248
249 pub fn translation_mode(&self) -> &TranslationMode {
251 &self.translation_mode
252 }
253
254 #[cfg(not(target_arch = "wasm32"))]
256 fn generate_function(&self, func: &serde_json::Value) -> Result<String> {
257 if let Some(source_code) = self.extract_python_source(func) {
259 match &self.translation_mode {
260 TranslationMode::AstBased => {
261 return self.translate_with_ast(&source_code);
262 }
263 TranslationMode::FeatureBased => {
264 return self.translate_with_features(&source_code);
265 }
266 _ => {}
267 }
268 }
269
270 let mut generator = CodeGenerator::new();
272 generator.generate_function(func)
273 }
274
275 #[cfg(not(target_arch = "wasm32"))]
277 fn translate_with_ast(&self, python_code: &str) -> Result<String> {
278 let parser = PythonParser::new(python_code.to_string(), "<string>".to_string());
280 let module = parser.parse()?;
281
282 let mut translator = PythonToRustTranslator::new();
284 translator.translate_module(&module)
285 }
286
287 #[cfg(not(target_arch = "wasm32"))]
289 fn translate_with_features(&self, python_code: &str) -> Result<String> {
290 let mut translator = FeatureTranslator::new();
291 translator.translate(python_code)
292 }
293
294 #[cfg(not(target_arch = "wasm32"))]
299 pub fn translate_python_module(&self, python_source: &str) -> Result<String> {
300 match &self.translation_mode {
301 TranslationMode::PatternBased => {
302 Err(Error::CodeGeneration(
305 "PatternBased mode requires pre-analyzed JSON input. Use AstBased or FeatureBased for raw Python source.".into()
306 ))
307 }
308 TranslationMode::AstBased => {
309 self.translate_with_ast(python_source)
310 }
311 TranslationMode::FeatureBased => {
312 self.translate_with_features(python_source)
313 }
314 #[cfg(feature = "nemo")]
315 TranslationMode::NeMo { service_url, mode, temperature } => {
316 Err(Error::CodeGeneration(
318 "NeMo mode requires async translation. Use translate_with_nemo directly.".into()
319 ))
320 }
321 }
322 }
323
324 #[cfg(feature = "nemo")]
326 async fn translate_with_nemo(
327 &self,
328 python_code: &str,
329 service_url: &str,
330 mode: &str,
331 temperature: f32,
332 ) -> Result<String> {
333 let client = NeMoClient::new(service_url)?;
334
335 let request = TranslateRequest {
336 python_code: python_code.to_string(),
337 mode: mode.to_string(),
338 temperature,
339 include_metrics: true,
340 };
341
342 let response = client.translate(request).await?;
343
344 tracing::info!(
345 "NeMo translation complete: confidence={:.2}%, gpu_util={:.1}%, time={:.1}ms",
346 response.confidence * 100.0,
347 response.metrics.gpu_utilization * 100.0,
348 response.metrics.total_time_ms
349 );
350
351 Ok(response.rust_code)
352 }
353
354 fn extract_python_source(&self, func: &serde_json::Value) -> Option<String> {
356 func.get("source_code")
357 .and_then(|s| s.as_str())
358 .map(|s| s.to_string())
359 }
360
361 #[allow(dead_code)]
363 #[cfg(not(target_arch = "wasm32"))]
364 fn generate_function_old(&self, func: &serde_json::Value) -> Result<String> {
365 let name = func.get("name")
366 .and_then(|n| n.as_str())
367 .ok_or_else(|| Error::CodeGeneration("Missing function name".into()))?;
368
369 let params: Vec<serde_json::Value> = func.get("params")
370 .and_then(|p| p.as_array())
371 .cloned()
372 .unwrap_or_default();
373
374 let return_type = func.get("return_type")
375 .and_then(|r| r.as_object())
376 .and_then(|r| {
377 if let Some(variant) = r.keys().next() {
379 Some(variant.as_str())
380 } else {
381 None
382 }
383 })
384 .unwrap_or("Unknown");
385
386 let return_str = match return_type {
388 "I32" => "i32",
389 "I64" => "i64",
390 "F64" => "f64",
391 "String" => "String",
392 "Bool" => "bool",
393 "Unknown" => "()",
394 _ => "()",
395 };
396
397 let param_strs: Vec<String> = params.iter().map(|p| {
399 let param_name = p.get("name")
400 .and_then(|n| n.as_str())
401 .unwrap_or("arg");
402
403 let rust_type = p.get("rust_type")
404 .and_then(|r| r.as_object())
405 .and_then(|r| r.keys().next().map(|s| s.as_str()))
406 .unwrap_or("I32");
407
408 let type_str = match rust_type {
409 "I32" => "i32",
410 "I64" => "i64",
411 "F64" => "f64",
412 "String" => "String",
413 "Bool" => "bool",
414 _ => "i32",
415 };
416
417 format!("{}: {}", param_name, type_str)
418 }).collect();
419
420 let params_str = param_strs.join(", ");
421
422 let mut code = String::new();
424 code.push_str(&format!("pub fn {}({}) -> {} {{\n", name, params_str, return_str));
425
426 if name == "add" && return_str == "i32" {
428 code.push_str(" a + b\n");
429 } else if name == "multiply" && return_str == "i32" {
430 code.push_str(" x * y\n");
431 } else if name == "subtract" && return_str == "i32" {
432 code.push_str(" a - b\n");
433 } else if name == "divide" && return_str == "i32" {
434 code.push_str(" a / b\n");
435 } else if name == "fibonacci" && return_str == "i32" {
436 code.push_str(" if n <= 1 {\n");
437 code.push_str(" return n;\n");
438 code.push_str(" }\n");
439 code.push_str(" fibonacci(n - 1) + fibonacci(n - 2)\n");
440 } else if return_str == "()" {
441 code.push_str(" // TODO: Implement function body\n");
443 } else {
444 code.push_str(" // TODO: Implement function body\n");
446 match return_str {
447 "i32" | "i64" => code.push_str(" 0\n"),
448 "f64" => code.push_str(" 0.0\n"),
449 "bool" => code.push_str(" false\n"),
450 "String" => code.push_str(" String::new()\n"),
451 _ => code.push_str(" Default::default()\n"),
452 }
453 }
454
455 code.push_str("}\n");
456
457 Ok(code)
458 }
459}
460
461#[cfg(not(target_arch = "wasm32"))]
462impl Default for TranspilerAgent {
463 fn default() -> Self {
464 Self::new()
465 }
466}
467
468#[cfg(not(target_arch = "wasm32"))]
469#[async_trait]
470impl Agent for TranspilerAgent {
471 type Input = TranspilerInput;
472 type Output = TranspilerOutput;
473
474 async fn execute(&self, input: Self::Input) -> Result<Self::Output> {
475 tracing::info!("Transpiling Python to Rust");
476
477 let mut rust_code = String::new();
478
479 rust_code.push_str("// Generated by Portalis Transpiler\n");
481 rust_code.push_str("#![allow(unused)]\n\n");
482
483 if !input.use_statements.is_empty() {
485 for use_stmt in &input.use_statements {
486 rust_code.push_str(use_stmt);
487 rust_code.push('\n');
488 }
489 rust_code.push('\n');
490 }
491
492 if !input.typed_classes.is_empty() {
494 let mut class_translator = ClassTranslator::new();
495 for class in &input.typed_classes {
496 let class_code = class_translator.generate_class(class)?;
497 rust_code.push_str(&class_code);
498 rust_code.push('\n');
499 }
500 }
501
502 for func in &input.typed_functions {
504 let func_code = self.generate_function(func)?;
505 rust_code.push_str(&func_code);
506 rust_code.push('\n');
507 }
508
509 let metadata = ArtifactMetadata::new(self.name())
510 .with_tag("functions", input.typed_functions.len().to_string())
511 .with_tag("classes", input.typed_classes.len().to_string())
512 .with_tag("dependencies", input.cargo_dependencies.len().to_string())
513 .with_tag("lines", rust_code.lines().count().to_string());
514
515 Ok(TranspilerOutput {
516 rust_code,
517 metadata,
518 })
519 }
520
521 fn id(&self) -> AgentId {
522 self.id
523 }
524
525 fn name(&self) -> &str {
526 "TranspilerAgent"
527 }
528
529 fn capabilities(&self) -> Vec<AgentCapability> {
530 vec![AgentCapability::CodeGeneration]
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537 use serde_json::json;
538
539 #[tokio::test]
540 async fn test_generate_simple_function() {
541 let agent = TranspilerAgent::new();
542
543 let input = TranspilerInput {
544 typed_functions: vec![json!({
545 "name": "add",
546 "params": [
547 {"name": "a", "rust_type": {"I32": null}},
548 {"name": "b", "rust_type": {"I32": null}}
549 ],
550 "return_type": {"I32": null}
551 })],
552 typed_classes: vec![],
553 use_statements: vec![],
554 cargo_dependencies: vec![],
555 api_contract: json!({}),
556 };
557
558 let output = agent.execute(input).await.unwrap();
559
560 assert!(output.rust_code.contains("pub fn add"));
561 assert!(output.rust_code.contains("a: i32, b: i32"));
562 assert!(output.rust_code.contains("-> i32"));
563 }
564
565 #[tokio::test]
566 async fn test_generate_fibonacci() {
567 let agent = TranspilerAgent::new();
568
569 let input = TranspilerInput {
570 typed_functions: vec![json!({
571 "name": "fibonacci",
572 "params": [
573 {"name": "n", "rust_type": {"I32": null}}
574 ],
575 "return_type": {"I32": null}
576 })],
577 typed_classes: vec![],
578 use_statements: vec![],
579 cargo_dependencies: vec![],
580 api_contract: json!({}),
581 };
582
583 let output = agent.execute(input).await.unwrap();
584
585 assert!(output.rust_code.contains("pub fn fibonacci"));
586 assert!(output.rust_code.contains("if n <= 1"));
587 assert!(output.rust_code.contains("fibonacci(n - 1) + fibonacci(n - 2)"));
588 }
589
590 #[test]
591 fn test_feature_based_translation() {
592 let agent = TranspilerAgent::with_feature_mode();
593
594 let python_code = r#"
595x = 42
596y = 3.14
597msg = "hello"
598result = x + 10
599"#;
600
601 let rust_code = agent.translate_python_module(python_code).unwrap();
602
603 assert!(rust_code.contains("let x: i32 = 42"));
604 assert!(rust_code.contains("let y: f64 = 3.14"));
605 assert!(rust_code.contains("let msg: String = \"hello\""));
606 assert!(rust_code.contains("let result: i32 = x + 10"));
607 }
608
609 #[test]
610 fn test_ast_based_translation() {
611 let agent = TranspilerAgent::with_ast_mode();
612
613 let python_code = r#"
614def add(a, b):
615 return a + b
616"#;
617
618 let rust_code = agent.translate_python_module(python_code).unwrap();
619
620 assert!(rust_code.contains("fn add"));
621 assert!(rust_code.contains("return a + b"));
622 }
623
624 #[test]
625 fn test_feature_based_with_imports() {
626 let agent = TranspilerAgent::with_feature_mode();
627
628 let python_code = r#"
629import math
630x = math.sqrt(16)
631"#;
632
633 let rust_code = agent.translate_python_module(python_code).unwrap();
634
635 assert!(rust_code.contains("use") || rust_code.contains("math"));
637 }
638
639 #[test]
640 fn test_feature_based_with_control_flow() {
641 let agent = TranspilerAgent::with_feature_mode();
642
643 let python_code = r#"
644x = 10
645if x > 5:
646 y = 20
647else:
648 y = 30
649"#;
650
651 let rust_code = agent.translate_python_module(python_code).unwrap();
652
653 assert!(rust_code.contains("let x: i32 = 10"));
654 assert!(rust_code.contains("if x > 5"));
655 }
656
657 #[tokio::test]
658 async fn test_ast_mode_with_source_code() {
659 let agent = TranspilerAgent::with_ast_mode();
660
661 let input = TranspilerInput {
662 typed_functions: vec![json!({
663 "name": "add",
664 "params": [
665 {"name": "a", "rust_type": {"I32": null}},
666 {"name": "b", "rust_type": {"I32": null}}
667 ],
668 "return_type": {"I32": null},
669 "source_code": "def add(a, b):\n return a + b"
670 })],
671 typed_classes: vec![],
672 use_statements: vec![],
673 cargo_dependencies: vec![],
674 api_contract: json!({}),
675 };
676
677 let output = agent.execute(input).await.unwrap();
678
679 assert!(output.rust_code.contains("fn add"));
681 }
682}