use std::fs;
use std::process::Command;
fn compile_rust_code(rust_code: &str, output_name: &str) -> Result<(), String> {
let temp_dir = std::env::temp_dir();
let temp_file = temp_dir.join(format!("{}.rs", output_name));
let output_file = temp_dir.join(output_name);
let wrapped_code = wrap_functions_in_unsafe(rust_code);
let final_code = format!("#![allow(unused)]\n\n{}", wrapped_code);
fs::write(&temp_file, final_code).expect("Failed to write temp file");
let compile_output = Command::new("rustc")
.arg(&temp_file)
.arg("--crate-type")
.arg("lib")
.arg("-o")
.arg(&output_file)
.output()
.expect("Failed to run rustc");
let _ = fs::remove_file(&temp_file);
let _ = fs::remove_file(&output_file);
if compile_output.status.success() {
Ok(())
} else {
Err(String::from_utf8_lossy(&compile_output.stderr).to_string())
}
}
fn wrap_functions_in_unsafe(rust_code: &str) -> String {
let mut result = String::new();
let mut in_function = false;
let mut brace_count = 0;
for line in rust_code.lines() {
let trimmed = line.trim();
if trimmed.starts_with("pub struct") || trimmed.starts_with("#[derive") {
result.push_str(line);
result.push('\n');
continue;
}
if trimmed.starts_with("fn ") && trimmed.contains('{') {
in_function = true;
brace_count = 1;
result.push_str(line);
result.push('\n');
result.push_str(" unsafe {\n");
continue;
}
if in_function {
brace_count += line.matches('{').count() as i32;
brace_count -= line.matches('}').count() as i32;
if brace_count == 0 {
result.push_str(" }\n");
result.push_str(line);
result.push('\n');
in_function = false;
} else {
result.push_str(" ");
result.push_str(line);
result.push('\n');
}
} else {
result.push_str(line);
result.push('\n');
}
}
result
}
#[test]
fn test_simple_struct_definition() {
let c_code = r#"
struct Point {
int x;
int y;
};
int get_x(struct Point* p) {
return p->x;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile simple struct, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
assert!(
rust_code.contains("struct Point") || rust_code.contains("Point"),
"Should contain Point struct definition"
);
assert!(rust_code.contains("x") && rust_code.contains("y"), "Should contain x and y fields");
assert!(rust_code.contains("fn get_x"), "Should contain get_x function");
match compile_rust_code(&rust_code, "test_simple_struct") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_struct_pointer_member_access() {
let c_code = r#"
struct Node {
int data;
struct Node* next;
};
int get_data(struct Node* node) {
if (node != 0) {
return node->data;
}
return -1;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile struct with pointers, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
assert!(
rust_code.contains("struct Node") || rust_code.contains("Node"),
"Should contain Node struct"
);
assert!(rust_code.contains("data"), "Should contain data field access");
match compile_rust_code(&rust_code, "test_struct_pointer_access") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_linked_list_real_world() {
let c_code = r#"
struct Node {
int data;
struct Node* next;
};
int list_length(struct Node* head) {
int count = 0;
while (head != 0) {
count = count + 1;
head = head->next;
}
return count;
}
int list_sum(struct Node* head) {
int sum = 0;
while (head != 0) {
sum = sum + head->data;
head = head->next;
}
return sum;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile linked list, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
assert!(rust_code.contains("fn list_length"), "Should contain list_length function");
assert!(rust_code.contains("fn list_sum"), "Should contain list_sum function");
match compile_rust_code(&rust_code, "test_linked_list") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_nested_structs() {
let c_code = r#"
struct Point {
int x;
int y;
};
struct Rectangle {
struct Point top_left;
struct Point bottom_right;
};
int rect_width(struct Rectangle* r) {
return r->bottom_right.x - r->top_left.x;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile nested structs, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
assert!(
rust_code.contains("Point") && rust_code.contains("Rectangle"),
"Should contain both struct definitions"
);
match compile_rust_code(&rust_code, "test_nested_structs") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_struct_with_sizeof() {
let c_code = r#"
struct Data {
int value;
int flags;
};
int get_struct_size() {
return sizeof(struct Data);
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile sizeof, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
assert!(
rust_code.contains("size_of") || rust_code.contains("std::mem"),
"Should use Rust's size_of equivalent"
);
match compile_rust_code(&rust_code, "test_sizeof") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}