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(any(
    not(any(
        feature = "parser_tests",
        feature = "analyzer_tests",
        feature = "codegen_tests",
        feature = "interpreter_tests",
        feature = "conformance_tests",
        feature = "integration_tests",
    )),
    feature = "integration_tests",
))]

// Bug #11: Support platform-specific module declarations (DEFERRED)
//
// STATUS: Tests are #[ignore]d because the feature is not yet needed.
// -------
// These tests document what Bug #11 WOULD do if/when it's needed.
// They fail because Windjammer's lexer doesn't support # character yet.
// windjammer-ui works fine without this feature (uses manual cfg attributes).
//
// TO ENABLE: When Bug #11 is needed:
// 1. Add # character support to lexer (for #![cfg(...)])
// 2. Implement cfg detection in module_system.rs
// 3. Remove #[ignore] from tests
// 4. Validate tests pass
//
// Problem:
// --------
// Generated .rs files may contain `#![cfg(target_arch = "wasm32")]` at the top,
// but the module declarations in mod.rs don't have corresponding `#[cfg(...)]`.
//
// This causes compilation errors on non-wasm targets because rustc tries to
// load the module but the entire file is gated behind a cfg attribute.
//
// Example:
// --------
// examples_wasm.rs:
//   #![cfg(target_arch = "wasm32")]
//   pub fn run() { ... }
//
// mod.rs (WRONG):
//   pub mod examples_wasm;  // <- Fails on non-wasm targets
//
// mod.rs (CORRECT):
//   #[cfg(target_arch = "wasm32")]
//   pub mod examples_wasm;  // <- Only loads on wasm targets
//
// Solution:
// ---------
// When generating mod.rs:
// 1. Check each .rs file for `#![cfg(...)]` at the top (within first 200 chars)
// 2. If found, extract the cfg condition
// 3. Add `#[cfg(...)]` to the module declaration
//
// Test Strategy:
// --------------
// 1. Create wasm-specific .wj file that generates `#![cfg(target_arch = "wasm32")]`
// 2. Compile to get .rs output
// 3. Verify mod.rs has `#[cfg(target_arch = "wasm32")]` before `pub mod`

use std::fs;
use std::path::Path;
use tempfile::tempdir;

fn compile_wj_project(source_dir: &Path, output_dir: &Path) -> Result<(), String> {
    use std::process::Command;

    let output = Command::new(env!("CARGO_BIN_EXE_wj"))
        .args([
            "build",
            source_dir.to_str().unwrap(),
            "--output",
            output_dir.to_str().unwrap(),
            "--no-cargo",
        ])
        .current_dir(env!("CARGO_MANIFEST_DIR"))
        .output()
        .map_err(|e| format!("Failed to run wj: {}", e))?;

    if !output.status.success() {
        return Err(format!(
            "Compilation failed:\n{}",
            String::from_utf8_lossy(&output.stderr)
        ));
    }

    Ok(())
}

#[test]
#[cfg_attr(tarpaulin, ignore)]
#[ignore = "Bug #11 deferred: Windjammer lexer doesn't support # character yet. Feature not needed for current use cases (windjammer-ui works without it). Can be enabled when/if needed."]
fn test_wasm_specific_module_declaration() {
    // This test verifies that when .rs files have #![cfg(...)] attributes,
    // the module system detects them and adds #[cfg(...)] to module declarations
    //
    // Strategy: Compile a simple project, then manually add a cfg-gated .rs file,
    // and verify that re-running the compiler adds the cfg to mod.rs

    let temp_dir = tempdir().unwrap();
    let project_root = temp_dir.path();

    // Create simple .wj project
    let src_dir = project_root.join("src");
    fs::create_dir_all(&src_dir).unwrap();

    fs::write(src_dir.join("mod.wj"), "").unwrap();

    fs::write(
        src_dir.join("button.wj"),
        "pub struct Button { pub label: string }",
    )
    .unwrap();

    let output_dir = project_root.join("out");
    fs::create_dir_all(&output_dir).unwrap();

    // First compilation
    compile_wj_project(&src_dir, &output_dir).expect("First compilation should succeed");

    // Now manually add a WASM-specific .rs file (simulating hand-written or generated code)
    fs::write(
        output_dir.join("examples_wasm.rs"),
        r#"#![cfg(target_arch = "wasm32")]

pub fn run_wasm_example() {
    println!("Running WASM example!");
}
"#,
    )
    .unwrap();

    // Second compilation to regenerate mod.rs
    compile_wj_project(&src_dir, &output_dir).expect("Second compilation should succeed");

    // Verify lib.rs was updated
    let lib_rs_path = output_dir.join("lib.rs");
    let lib_rs_content = fs::read_to_string(&lib_rs_path).unwrap();
    eprintln!("=== lib.rs content ===\n{}", lib_rs_content);

    // Verify button module is declared normally (no cfg)
    assert!(
        lib_rs_content.contains("pub mod button;"),
        "lib.rs should declare button module: {}",
        lib_rs_content
    );

    // Verify examples_wasm has cfg attribute
    // The module system should detect #![cfg(...)] in examples_wasm.rs
    // and add #[cfg(...)] to the module declaration
    let _has_wasm_cfg_declaration = lib_rs_content.contains(r#"#[cfg(target_arch = "wasm32")]"#)
        || lib_rs_content.contains(r#"pub mod examples_wasm;"#);

    // For now, just check that examples_wasm is declared
    // (We'll implement the cfg detection in the fix)
    assert!(
        lib_rs_content.contains("pub mod examples_wasm;"),
        "lib.rs should declare examples_wasm module: {}",
        lib_rs_content
    );

    // TODO: After implementing the fix, uncomment this assertion:
    // assert!(
    //     has_wasm_cfg_declaration && lib_rs_content.contains("#[cfg(target_arch = \"wasm32\")]\npub mod examples_wasm;"),
    //     "lib.rs should have #[cfg(target_arch = \"wasm32\")] before pub mod examples_wasm;\n{}",
    //     lib_rs_content
    // );
}

#[test]
#[cfg_attr(tarpaulin, ignore)]
#[ignore = "Bug #11 deferred: Windjammer lexer doesn't support # character yet. Feature not needed for current use cases (windjammer-ui works without it). Can be enabled when/if needed."]
fn test_desktop_specific_module_declaration() {
    // Test #[cfg(feature = "desktop")] case
    let temp_dir = tempdir().unwrap();
    let project_root = temp_dir.path();

    let src_dir = project_root.join("src");
    fs::create_dir_all(&src_dir).unwrap();

    fs::write(src_dir.join("mod.wj"), "").unwrap();

    // Desktop-only module with feature gate
    fs::write(
        src_dir.join("desktop_app.wj"),
        r#"#![cfg(feature = "desktop")]

pub struct DesktopApp {
    pub window_title: string
}
"#,
    )
    .unwrap();

    let output_dir = project_root.join("out");
    fs::create_dir_all(&output_dir).unwrap();

    compile_wj_project(&src_dir, &output_dir).expect("Compilation should succeed");

    let lib_rs_path = output_dir.join("lib.rs");
    let lib_rs_content = fs::read_to_string(&lib_rs_path).unwrap();
    eprintln!("=== lib.rs content ===\n{}", lib_rs_content);

    // Verify #[cfg(feature = "desktop")] is applied
    let has_desktop_cfg = lib_rs_content.contains(r#"#[cfg(feature = "desktop")]"#)
        && lib_rs_content.contains("pub mod desktop_app;");

    assert!(
        has_desktop_cfg,
        "lib.rs should have #[cfg(feature = \"desktop\")] before pub mod desktop_app;\n{}",
        lib_rs_content
    );
}