windjammer 0.48.0

A simple language inspired by Go, Ruby, and Elixir that transpiles to Rust - 80% of Rust's power with 20% of the complexity
Documentation
#![cfg(not(any(
    feature = "parser_tests",
    feature = "analyzer_tests",
    feature = "codegen_tests",
    feature = "interpreter_tests",
    feature = "conformance_tests",
    feature = "integration_tests",
)))]

/// TDD Test: Nested Field Access Through Borrowed Iterator
///
/// Bug: Compiler fails to handle nested field access like stack.item.id
/// where stack is from &collection, needs to generate &stack.item.id or stack.item.id.clone()
/// depending on parameter ownership.
///
/// Example:
/// ```windjammer
/// for stack in &stacks {
///     has_item(stack.item.id)  // stack.item.id is nested field access
/// }
/// ```
use std::fs;
use std::process::Command;
use tempfile::TempDir;

#[test]
fn test_nested_field_borrowed_param() {
    // Windjammer source with nested field access
    let source = r#"
struct Item {
    id: string
    name: string
}

struct Stack {
    item: Item
    quantity: i32
}

fn has_item(item_id: string, quantity: i32) -> bool {
    true
}

fn check_stacks(stacks: Vec<Stack>) -> bool {
    for stack in &stacks {
        if !has_item(stack.item.id, stack.quantity) {
            return false
        }
    }
    true
}

fn main() {
    let stacks = Vec::new()
    let result = check_stacks(stacks)
}
"#;

    // Generate Rust code
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let wj_file = temp_dir.path().join("test.wj");
    fs::write(&wj_file, source).unwrap();

    let out_dir = temp_dir.path().join("out");

    // Compile with wj
    let wj_binary = env!("CARGO_BIN_EXE_wj");
    let _output = Command::new(wj_binary)
        .arg("build")
        .arg("--no-cargo")
        .arg(&wj_file)
        .arg("--target")
        .arg("rust")
        .arg("--output")
        .arg(&out_dir)
        .output()
        .expect("Failed to run wj compiler");

    // Read generated Rust
    let rust_file = out_dir.join("test.rs");
    let generated = fs::read_to_string(&rust_file).expect("Failed to read generated Rust file");

    println!("Generated Rust code:\n{}", generated);

    // Compile with rustc
    let rustc_output = Command::new("rustc")
        .arg(&rust_file)
        .arg("--crate-type")
        .arg("bin")
        .arg("--edition")
        .arg("2021")
        .arg("--out-dir")
        .arg(temp_dir.path())
        .output()
        .expect("Failed to run rustc");

    if !rustc_output.status.success() {
        let stderr = String::from_utf8_lossy(&rustc_output.stderr);
        panic!(
            "Rustc compilation failed:\n{}\n\nGenerated code:\n{}",
            stderr, generated
        );
    }

    // Verify generated code has correct ownership handling
    // Should generate: has_item(&stack.item.id, stack.quantity)
    // NOT: has_item(stack.item.id, stack.quantity) ← would cause E0507
    assert!(
        generated.contains("&stack.item.id") || generated.contains("stack.item.id.clone()"),
        "Should add & or .clone() for nested field access through borrowed iterator"
    );
}

#[test]
fn test_nested_field_owned_param() {
    // Windjammer source where parameter expects owned String
    let source = r#"
struct Item {
    id: string
}

struct Stack {
    item: Item
}

fn remove_item(item_id: string) {
    let mut removed: Vec<string> = Vec::new()
    removed.push(item_id)
}

fn process(stacks: Vec<Stack>) {
    for stack in &stacks {
        remove_item(stack.item.id)  // Should clone for owned param
    }
}

fn main() {}
"#;

    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let wj_file = temp_dir.path().join("test.wj");
    fs::write(&wj_file, source).unwrap();

    let out_dir = temp_dir.path().join("out");

    let wj_binary = env!("CARGO_BIN_EXE_wj");
    let _output = Command::new(wj_binary)
        .arg("build")
        .arg("--no-cargo")
        .arg(&wj_file)
        .arg("--target")
        .arg("rust")
        .arg("--output")
        .arg(&out_dir)
        .output()
        .expect("Failed to run wj compiler");

    let rust_file = out_dir.join("test.rs");
    let generated = fs::read_to_string(&rust_file).expect("Failed to read generated Rust file");

    println!("Generated Rust code:\n{}", generated);

    // Compile with rustc
    let rustc_output = Command::new("rustc")
        .arg(&rust_file)
        .arg("--crate-type")
        .arg("bin")
        .arg("--edition")
        .arg("2021")
        .arg("--out-dir")
        .arg(temp_dir.path())
        .output()
        .expect("Failed to run rustc");

    if !rustc_output.status.success() {
        let stderr = String::from_utf8_lossy(&rustc_output.stderr);
        panic!(
            "Rustc compilation failed:\n{}\n\nGenerated code:\n{}",
            stderr, generated
        );
    }

    // Should generate: remove_item(stack.item.id.clone())
    // Because remove_item takes owned String, not &String
    assert!(
        generated.contains("stack.item.id.clone()"),
        "Should add .clone() for nested field when parameter expects owned"
    );
}